PyImageLabeling 1.0.0__py3-none-any.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.
- PyImageLabeling/__init__.py +22 -0
- PyImageLabeling/config.json +289 -0
- PyImageLabeling/controller/Controller.py +25 -0
- PyImageLabeling/controller/Events.py +147 -0
- PyImageLabeling/controller/FileEvents.py +69 -0
- PyImageLabeling/controller/ImageEvents.py +32 -0
- PyImageLabeling/controller/LabelEvents.py +219 -0
- PyImageLabeling/controller/LabelingEvents.py +123 -0
- PyImageLabeling/controller/settings/ContourFillinSetting.py +93 -0
- PyImageLabeling/controller/settings/CoutourFillingApplyCancel.py +37 -0
- PyImageLabeling/controller/settings/EraserSetting.py +73 -0
- PyImageLabeling/controller/settings/LabelSetting.py +91 -0
- PyImageLabeling/controller/settings/MagicPenSetting.py +125 -0
- PyImageLabeling/controller/settings/OpacitySetting.py +66 -0
- PyImageLabeling/controller/settings/PaintBrushSetting.py +66 -0
- PyImageLabeling/icons/apply.png +0 -0
- PyImageLabeling/icons/asterisk-green.png +0 -0
- PyImageLabeling/icons/asterisk-red.png +0 -0
- PyImageLabeling/icons/back.png +0 -0
- PyImageLabeling/icons/border.png +0 -0
- PyImageLabeling/icons/cancel.png +0 -0
- PyImageLabeling/icons/cleaner.png +0 -0
- PyImageLabeling/icons/close.png +0 -0
- PyImageLabeling/icons/down.png +0 -0
- PyImageLabeling/icons/ellipse.png +0 -0
- PyImageLabeling/icons/eraser.png +0 -0
- PyImageLabeling/icons/filling.png +0 -0
- PyImageLabeling/icons/logoMAIA.png +0 -0
- PyImageLabeling/icons/magic.png +0 -0
- PyImageLabeling/icons/maia.png +0 -0
- PyImageLabeling/icons/maia1.png +0 -0
- PyImageLabeling/icons/maia3.ico +0 -0
- PyImageLabeling/icons/maia_icon.png +0 -0
- PyImageLabeling/icons/move.png +0 -0
- PyImageLabeling/icons/opacity.png +0 -0
- PyImageLabeling/icons/open_image.png +0 -0
- PyImageLabeling/icons/open_layer.png +0 -0
- PyImageLabeling/icons/paint.png +0 -0
- PyImageLabeling/icons/plus.png +0 -0
- PyImageLabeling/icons/polygon.png +0 -0
- PyImageLabeling/icons/rectangle.png +0 -0
- PyImageLabeling/icons/reset.png +0 -0
- PyImageLabeling/icons/save.png +0 -0
- PyImageLabeling/icons/setting.png +0 -0
- PyImageLabeling/icons/transparency.png:Zone.Identifier +4 -0
- PyImageLabeling/icons/up.png +0 -0
- PyImageLabeling/icons/visibility.png +0 -0
- PyImageLabeling/icons/zoom_minus.png +0 -0
- PyImageLabeling/icons/zoom_plus.png +0 -0
- PyImageLabeling/model/Core.py +795 -0
- PyImageLabeling/model/File/Files.py +166 -0
- PyImageLabeling/model/File/NextImage.py +36 -0
- PyImageLabeling/model/File/PreviousImage.py +19 -0
- PyImageLabeling/model/Image/MoveImage.py +32 -0
- PyImageLabeling/model/Image/ResetMoveZoomImage.py +16 -0
- PyImageLabeling/model/Image/ZoomMinus.py +25 -0
- PyImageLabeling/model/Image/ZoomPlus.py +16 -0
- PyImageLabeling/model/Labeling/ClearAll.py +22 -0
- PyImageLabeling/model/Labeling/ContourFilling.py +135 -0
- PyImageLabeling/model/Labeling/Ellipse.py +350 -0
- PyImageLabeling/model/Labeling/Eraser.py +131 -0
- PyImageLabeling/model/Labeling/MagicPen.py +131 -0
- PyImageLabeling/model/Labeling/PaintBrush.py +207 -0
- PyImageLabeling/model/Labeling/Polygon.py +279 -0
- PyImageLabeling/model/Labeling/Rectangle.py +248 -0
- PyImageLabeling/model/Labeling/Undo.py +12 -0
- PyImageLabeling/model/Model.py +40 -0
- PyImageLabeling/model/Utils.py +40 -0
- PyImageLabeling/old_version/label_rectangle_properties.json +6 -0
- PyImageLabeling/old_version/main.py +2073 -0
- PyImageLabeling/old_version/models/EraseSettingsDialog.py +51 -0
- PyImageLabeling/old_version/models/LabeledRectangle.py +80 -0
- PyImageLabeling/old_version/models/MagicSettingsDialog.py +119 -0
- PyImageLabeling/old_version/models/OverlayOpacityDialog.py +63 -0
- PyImageLabeling/old_version/models/PaintSettingsDialog.py +289 -0
- PyImageLabeling/old_version/models/PointItem.py +66 -0
- PyImageLabeling/old_version/models/ProcessWorker.py +52 -0
- PyImageLabeling/old_version/models/ZoomableGraphicsView.py +1214 -0
- PyImageLabeling/old_version/models/tools/ContourTool.py +279 -0
- PyImageLabeling/old_version/models/tools/EraserTool.py +290 -0
- PyImageLabeling/old_version/models/tools/MagicPenTool.py +199 -0
- PyImageLabeling/old_version/models/tools/OverlayTool.py +179 -0
- PyImageLabeling/old_version/models/tools/PaintTool.py +68 -0
- PyImageLabeling/old_version/models/tools/PolygonTool.py +786 -0
- PyImageLabeling/old_version/models/tools/RectangleTool.py +1036 -0
- PyImageLabeling/parameters.json +1 -0
- PyImageLabeling/style.css +611 -0
- PyImageLabeling/view/Builder.py +333 -0
- PyImageLabeling/view/QBackgroundItem.py +30 -0
- PyImageLabeling/view/QWidgets.py +10 -0
- PyImageLabeling/view/View.py +226 -0
- PyImageLabeling/view/ZoomableGraphicsView.py +91 -0
- PyImageLabeling/view/__init__.py +0 -0
- pyimagelabeling-1.0.0.dist-info/METADATA +55 -0
- pyimagelabeling-1.0.0.dist-info/RECORD +99 -0
- pyimagelabeling-1.0.0.dist-info/WHEEL +5 -0
- pyimagelabeling-1.0.0.dist-info/licenses/LICENCE +22 -0
- pyimagelabeling-1.0.0.dist-info/top_level.txt +2 -0
- pypi/publish_pypi.py +18 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from PyQt6.QtWidgets import QVBoxLayout, QWidget, QListWidget, QHBoxLayout, QPushButton, QStatusBar, QGroupBox, QLayout, QStackedLayout, QLabel, QScrollArea, QGridLayout, QProgressBar
|
|
6
|
+
from PyQt6.QtGui import QIcon
|
|
7
|
+
from PyQt6.QtCore import Qt, QSize, QRect
|
|
8
|
+
from PyImageLabeling.model.Utils import Utils
|
|
9
|
+
|
|
10
|
+
from PyImageLabeling.view.ZoomableGraphicsView import ZoomableGraphicsView
|
|
11
|
+
|
|
12
|
+
from PyImageLabeling.view.QWidgets import QBlanckWidget1, QSeparator1
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
class Builder:
|
|
16
|
+
|
|
17
|
+
def __init__(self, view):
|
|
18
|
+
self.view = view
|
|
19
|
+
|
|
20
|
+
def build(self):
|
|
21
|
+
|
|
22
|
+
# Central widget
|
|
23
|
+
self.view.central_widget = QWidget()
|
|
24
|
+
self.view.setCentralWidget(self.view.central_widget)
|
|
25
|
+
self.view.main_layout = QGridLayout(self.view.central_widget)
|
|
26
|
+
self.view.main_layout.setSpacing(0)
|
|
27
|
+
self.build_labeling_bar()
|
|
28
|
+
self.build_graphics_view()
|
|
29
|
+
self.build_label_bar()
|
|
30
|
+
self.build_image_bar()
|
|
31
|
+
self.build_file_bar()
|
|
32
|
+
self.build_status_bar()
|
|
33
|
+
|
|
34
|
+
#self.build_apply_cancel_bar()
|
|
35
|
+
|
|
36
|
+
# def build_apply_cancel_bar(self):
|
|
37
|
+
# self.apply_cancel_bar_container = QWidget()
|
|
38
|
+
# apply_cancel_bar_layout = QHBoxLayout(self.apply_cancel_bar_container)
|
|
39
|
+
|
|
40
|
+
# for button in self.view.config["apply_cancel_bar"]:
|
|
41
|
+
# button_name = button["name"]
|
|
42
|
+
# self.view.buttons_apply_cancel_bar[button_name] = QPushButton()
|
|
43
|
+
# self.view.buttons_apply_cancel_bar[button_name].setToolTip(button["tooltip"]) # Detailed tooltip
|
|
44
|
+
# self.view.buttons_apply_cancel_bar[button_name].setObjectName("permanent")
|
|
45
|
+
# icon_path = Utils.get_icon_path(button["icon"])
|
|
46
|
+
# if os.path.exists(icon_path):
|
|
47
|
+
# self.view.buttons_apply_cancel_bar[button_name].setIcon(QIcon(icon_path))
|
|
48
|
+
# self.view.buttons_apply_cancel_bar[button_name].setIconSize(QSize(*self.view.config["window_size"]["icon"]))
|
|
49
|
+
# self.view.buttons_apply_cancel_bar[button_name].clicked.connect(getattr(self.view.controller, button["name"]))
|
|
50
|
+
# self.view.buttons_apply_cancel_bar[button_name].setCheckable(button["checkable"])
|
|
51
|
+
|
|
52
|
+
# apply_cancel_bar_layout.addWidget(self.view.buttons_apply_cancel_bar[button_name])
|
|
53
|
+
# self.apply_cancel_bar_container.setGeometry(QRect(0, 0, 200, 220))
|
|
54
|
+
# self.view.main_layout.addWidget(self.apply_cancel_bar_container)
|
|
55
|
+
|
|
56
|
+
def build_status_bar(self):
|
|
57
|
+
self.view.statusBar().showMessage('Ready')
|
|
58
|
+
self.view.progressBar = QProgressBar()
|
|
59
|
+
self.view.statusBar().addPermanentWidget(self.view.progressBar)
|
|
60
|
+
|
|
61
|
+
def build_file_bar(self):
|
|
62
|
+
self.view.file_bar_container = QWidget()
|
|
63
|
+
self.view.file_bar_layout = QVBoxLayout(self.view.file_bar_container)
|
|
64
|
+
self.file_bar_scroll = QScrollArea()
|
|
65
|
+
self.file_bar_button_container = QWidget()
|
|
66
|
+
self.file_bar_button_container.setObjectName("file_bar")
|
|
67
|
+
|
|
68
|
+
self.file_bar_button_layout = QHBoxLayout(self.file_bar_button_container)
|
|
69
|
+
|
|
70
|
+
for button in self.view.config["file_bar"]:
|
|
71
|
+
button_name = button["name"]
|
|
72
|
+
self.view.buttons_file_bar[button_name] = QPushButton()
|
|
73
|
+
self.view.buttons_file_bar[button_name].setToolTip(button["tooltip"]) # Detailed tooltip
|
|
74
|
+
self.view.buttons_file_bar[button_name].setObjectName("permanent")
|
|
75
|
+
icon_path = Utils.get_icon_path(button["icon"])
|
|
76
|
+
if os.path.exists(icon_path):
|
|
77
|
+
self.view.buttons_file_bar[button_name].setIcon(QIcon(icon_path))
|
|
78
|
+
self.view.buttons_file_bar[button_name].setIconSize(QSize(*self.view.config["window_size"]["icon"]))
|
|
79
|
+
self.view.buttons_file_bar[button_name].clicked.connect(getattr(self.view.controller, button["name"]))
|
|
80
|
+
self.view.buttons_file_bar[button_name].setCheckable(button["checkable"])
|
|
81
|
+
if 'previous' in button_name or 'next' in button_name or 'save' in button_name:
|
|
82
|
+
self.view.buttons_file_bar[button_name].setEnabled(False)
|
|
83
|
+
|
|
84
|
+
self.file_bar_button_layout.addWidget(self.view.buttons_file_bar[button_name])
|
|
85
|
+
self.view.file_bar_layout.addWidget(self.file_bar_button_container)
|
|
86
|
+
self.file_bar_button_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
|
87
|
+
|
|
88
|
+
self.view.file_bar_list = QListWidget()
|
|
89
|
+
self.view.file_bar_list.itemSelectionChanged.connect(self.view.file_bar_select)
|
|
90
|
+
#self.view.file_bar_list.itemClicked.connect(self.view.controller.on_file_double_clicked)
|
|
91
|
+
"""
|
|
92
|
+
self.file_bar_list.addItem("un tres llllllllllllllllllllllllloooooooooooooooooonnnnnnnnnnnnnnnnnnnng fichier.png")
|
|
93
|
+
for i in range(100):
|
|
94
|
+
self.file_bar_list.addItem("file_"+str(i)+".png")
|
|
95
|
+
"""
|
|
96
|
+
self.view.file_bar_list.setMinimumWidth(0)
|
|
97
|
+
|
|
98
|
+
self.view.file_bar_layout.addWidget(self.view.file_bar_list)
|
|
99
|
+
self.view.file_bar_layout.setSpacing(0)
|
|
100
|
+
self.view.file_bar_layout.setContentsMargins(0,0,0,self.view.config["window_size"]["margin"])
|
|
101
|
+
|
|
102
|
+
self.view.file_bar_container.setMinimumWidth(self.view.config["window_size"]["file_bar"]["width"])
|
|
103
|
+
self.view.file_bar_container.setMaximumWidth(self.view.config["window_size"]["file_bar"]["width"])
|
|
104
|
+
|
|
105
|
+
self.view.main_layout.addWidget(self.view.file_bar_container, 0, 3, 2, 1)
|
|
106
|
+
|
|
107
|
+
def build_labeling_bar(self):
|
|
108
|
+
self.labeling_bar_container = QWidget()
|
|
109
|
+
|
|
110
|
+
labeling_bar_layout = QVBoxLayout(self.labeling_bar_container)
|
|
111
|
+
#left_layout.setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
|
|
112
|
+
|
|
113
|
+
setting_buttons = {}
|
|
114
|
+
for category in self.view.config["labeling_bar_setting"]:
|
|
115
|
+
category_name = tuple(category.keys())[0]
|
|
116
|
+
if "Setting" in category_name:
|
|
117
|
+
for button in category[category_name]:
|
|
118
|
+
# Extract the base name (remove "_setting" suffix)
|
|
119
|
+
base_name = button["name"].replace("_setting", "")
|
|
120
|
+
setting_buttons[base_name] = button
|
|
121
|
+
|
|
122
|
+
for category_key in self.view.config["labeling_bar"].keys():
|
|
123
|
+
category = self.view.config["labeling_bar"][category_key]
|
|
124
|
+
category_name = category["name_view"]
|
|
125
|
+
frame = QGroupBox()
|
|
126
|
+
frame.setTitle(category_name)
|
|
127
|
+
buttons_layout = QVBoxLayout(frame)
|
|
128
|
+
|
|
129
|
+
for button in category["buttons"]:
|
|
130
|
+
button_name = button["name"]
|
|
131
|
+
self.view.buttons_labeling_bar[button_name] = QPushButton(button["name_view"])
|
|
132
|
+
self.view.buttons_labeling_bar[button_name].setToolTip(button["tooltip"]) # Detailed tooltip
|
|
133
|
+
icon_path = Utils.get_icon_path(button["icon"])
|
|
134
|
+
if os.path.exists(icon_path):
|
|
135
|
+
self.view.buttons_labeling_bar[button_name].setIcon(QIcon(icon_path))
|
|
136
|
+
self.view.buttons_labeling_bar[button_name].setIconSize(QSize(*self.view.config["window_size"]["icon"]))
|
|
137
|
+
self.view.buttons_labeling_bar[button_name].clicked.connect(getattr(self.view.controller, button["name"]))
|
|
138
|
+
self.view.buttons_labeling_bar[button_name].setCheckable(button["checkable"])
|
|
139
|
+
self.view.buttons_labeling_bar[button_name].setEnabled(False)
|
|
140
|
+
|
|
141
|
+
if button_name in setting_buttons:
|
|
142
|
+
setting_button_config = setting_buttons[button_name]
|
|
143
|
+
|
|
144
|
+
# Create horizontal layout: tool button + setting button
|
|
145
|
+
h_layout = QHBoxLayout()
|
|
146
|
+
self.view.buttons_labeling_bar[button_name].setObjectName("with_parameters")
|
|
147
|
+
h_layout.addWidget(self.view.buttons_labeling_bar[button_name])
|
|
148
|
+
|
|
149
|
+
# Create setting button
|
|
150
|
+
setting_button_name = setting_button_config["name"]
|
|
151
|
+
self.view.buttons_labeling_bar[setting_button_name] = QPushButton()
|
|
152
|
+
self.view.buttons_labeling_bar[setting_button_name].setObjectName("setting_button")
|
|
153
|
+
self.view.buttons_labeling_bar[setting_button_name].setToolTip(setting_button_config["tooltip"])
|
|
154
|
+
|
|
155
|
+
setting_icon_path = Utils.get_icon_path(setting_button_config["icon"])
|
|
156
|
+
if os.path.exists(setting_icon_path):
|
|
157
|
+
self.view.buttons_labeling_bar[setting_button_name].setIcon(QIcon(setting_icon_path))
|
|
158
|
+
self.view.buttons_labeling_bar[setting_button_name].setIconSize(QSize(*self.view.config["window_size"]["icon"]))
|
|
159
|
+
|
|
160
|
+
self.view.buttons_labeling_bar[setting_button_name].clicked.connect(getattr(self.view.controller, setting_button_name))
|
|
161
|
+
self.view.buttons_labeling_bar[setting_button_name].setCheckable(setting_button_config["checkable"])
|
|
162
|
+
self.view.buttons_labeling_bar[setting_button_name].setEnabled(False)
|
|
163
|
+
|
|
164
|
+
h_layout.setSpacing(0)
|
|
165
|
+
h_layout.addWidget(self.view.buttons_labeling_bar[setting_button_name])
|
|
166
|
+
|
|
167
|
+
buttons_layout.addLayout(h_layout)
|
|
168
|
+
else:
|
|
169
|
+
# No setting button, add main button directly
|
|
170
|
+
self.view.buttons_labeling_bar[button_name].setObjectName("without_parameters")
|
|
171
|
+
buttons_layout.addWidget(self.view.buttons_labeling_bar[button_name])
|
|
172
|
+
|
|
173
|
+
labeling_bar_layout.addWidget(frame)
|
|
174
|
+
self.labeling_bar_container.setMinimumWidth(self.view.config["window_size"]["labeling_bar"]["width"])
|
|
175
|
+
self.labeling_bar_container.setMaximumWidth(self.view.config["window_size"]["labeling_bar"]["width"])
|
|
176
|
+
labeling_bar_layout.setContentsMargins(0,0,0,self.view.config["window_size"]["margin"])
|
|
177
|
+
labeling_bar_layout.setSpacing(self.view.config["window_size"]["margin"])
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
labeling_bar_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
|
181
|
+
self.view.main_layout.addWidget(self.labeling_bar_container, 0, 0)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def build_graphics_view(self):
|
|
185
|
+
self.graphics_view_container = QWidget()
|
|
186
|
+
self.graphics_view_layout = QStackedLayout(self.graphics_view_container)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
self.view.zoomable_graphics_view = ZoomableGraphicsView(self.view)
|
|
190
|
+
self.view.zoomable_graphics_view.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
|
191
|
+
self.graphics_view_layout.addWidget(self.view.zoomable_graphics_view)
|
|
192
|
+
|
|
193
|
+
self.view.main_layout.addWidget(self.graphics_view_container, 0, 1, 2, 2)
|
|
194
|
+
self.graphics_view_container.setMinimumWidth(self.view.config["window_size"]["graphics_view"]["width"])
|
|
195
|
+
|
|
196
|
+
#self.right_layout_container.setMinimumSize(self.view.right_panel_width, self.view.right_panel_height)
|
|
197
|
+
|
|
198
|
+
def build_image_bar(self):
|
|
199
|
+
self.image_bar_container_1 = QWidget()
|
|
200
|
+
|
|
201
|
+
self.image_bar_container_1.setMinimumHeight(self.view.config["window_size"]["image_bar"]["height"])
|
|
202
|
+
self.image_bar_container_1.setMaximumHeight(self.view.config["window_size"]["image_bar"]["height"])
|
|
203
|
+
|
|
204
|
+
self.view.image_bar_layout_1 = QVBoxLayout(self.image_bar_container_1)
|
|
205
|
+
|
|
206
|
+
self.image_bar_container_2 = QWidget()
|
|
207
|
+
self.image_bar_container_2.setObjectName("image_bar")
|
|
208
|
+
self.view.image_bar_layout_1.addWidget(self.image_bar_container_2)
|
|
209
|
+
self.view.image_bar_layout_1.setContentsMargins(0,0,0,self.view.config["window_size"]["margin"])
|
|
210
|
+
|
|
211
|
+
self.view.image_bar_layout_1.setAlignment(Qt.AlignmentFlag.AlignRight)
|
|
212
|
+
|
|
213
|
+
self.view.image_bar_layout_2 = QVBoxLayout(self.image_bar_container_2)
|
|
214
|
+
|
|
215
|
+
self.image_bar_container_2.setMinimumWidth(self.view.config["window_size"]["image_bar"]["width"])
|
|
216
|
+
self.image_bar_container_2.setMaximumWidth(self.view.config["window_size"]["image_bar"]["width"])
|
|
217
|
+
|
|
218
|
+
for button in self.view.config["image_bar"]:
|
|
219
|
+
button_name = button["name"]
|
|
220
|
+
self.view.buttons_image_bar[button_name] = QPushButton()
|
|
221
|
+
self.view.buttons_image_bar[button_name].setObjectName("permanent")
|
|
222
|
+
self.view.buttons_image_bar[button_name].setToolTip(button["tooltip"])
|
|
223
|
+
icon_path = Utils.get_icon_path(button["icon"])
|
|
224
|
+
if os.path.exists(icon_path):
|
|
225
|
+
self.view.buttons_image_bar[button_name].setIcon(QIcon(icon_path))
|
|
226
|
+
self.view.buttons_image_bar[button_name].setIconSize(QSize(*self.view.config["window_size"]["icon"]))
|
|
227
|
+
self.view.buttons_image_bar[button_name].clicked.connect(getattr(self.view.controller, button["name"]))
|
|
228
|
+
self.view.buttons_image_bar[button_name].setCheckable(button["checkable"])
|
|
229
|
+
self.view.buttons_image_bar[button_name].setEnabled(False)
|
|
230
|
+
self.view.image_bar_layout_2.addWidget(self.view.buttons_image_bar[button_name])
|
|
231
|
+
|
|
232
|
+
self.view.image_bar_layout_2.setAlignment(Qt.AlignmentFlag.AlignRight)
|
|
233
|
+
self.view.main_layout.addWidget(self.image_bar_container_1, 1, 0)
|
|
234
|
+
|
|
235
|
+
def build_label_bar(self):
|
|
236
|
+
self.label_bar_container = QWidget()
|
|
237
|
+
self.label_bar_scroll = QScrollArea()
|
|
238
|
+
self.label_bar_container.setObjectName("label_bar")
|
|
239
|
+
self.view.label_bar_layout = QHBoxLayout(self.label_bar_container)
|
|
240
|
+
|
|
241
|
+
self.view.label_bar_layout.addWidget(QBlanckWidget1())
|
|
242
|
+
|
|
243
|
+
for button in self.view.config["label_bar"]["permanent"]:
|
|
244
|
+
button_name = button["name"]
|
|
245
|
+
self.view.buttons_label_bar_permanent[button_name] = QPushButton()
|
|
246
|
+
self.view.buttons_label_bar_permanent[button_name].setObjectName("permanent")
|
|
247
|
+
self.view.buttons_label_bar_permanent[button_name].setToolTip(button["tooltip"])
|
|
248
|
+
self.view.buttons_label_bar_permanent[button_name].setCheckable(button["checkable"])
|
|
249
|
+
self.view.buttons_label_bar_permanent[button_name].setEnabled(False)
|
|
250
|
+
|
|
251
|
+
icon_path = Utils.get_icon_path(button["icon"])
|
|
252
|
+
if os.path.exists(icon_path):
|
|
253
|
+
self.view.buttons_label_bar_permanent[button_name].setIcon(QIcon(icon_path))
|
|
254
|
+
self.view.buttons_label_bar_permanent[button_name].setIconSize(QSize(*self.view.config["window_size"]["icon"]))
|
|
255
|
+
self.view.buttons_label_bar_permanent[button_name].clicked.connect(getattr(self.view.controller, button["name"]))
|
|
256
|
+
self.view.label_bar_layout.addWidget(self.view.buttons_label_bar_permanent[button_name])
|
|
257
|
+
|
|
258
|
+
self.view.label_bar_layout.setContentsMargins(0,0,0,0)
|
|
259
|
+
self.view.label_bar_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
|
260
|
+
|
|
261
|
+
self.label_bar_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
262
|
+
self.label_bar_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
|
263
|
+
self.label_bar_scroll.setWidgetResizable(True)
|
|
264
|
+
self.label_bar_scroll.setWidget(self.label_bar_container)
|
|
265
|
+
self.label_bar_scroll.setMaximumHeight(self.view.config["window_size"]["label_bar"]["height"])
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
self.view.main_layout.addWidget(self.label_bar_scroll, 2, 0, 1, 4)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def build_new_layer_label_bar(self, label_id, name, labeling_mode, color):
|
|
275
|
+
|
|
276
|
+
push_buttons = dict() # Dictionnary of push buttons for this label
|
|
277
|
+
|
|
278
|
+
new_layer_label_bar_container = QWidget()
|
|
279
|
+
new_layer_label_bar_container.setObjectName("label_bar_new")
|
|
280
|
+
|
|
281
|
+
new_layer_label_bar_layout = QHBoxLayout(new_layer_label_bar_container)
|
|
282
|
+
new_layer_label_bar_layout.setContentsMargins(0,0,0,0)
|
|
283
|
+
new_layer_label_bar_layout.setSpacing(0)
|
|
284
|
+
new_layer_label_bar_layout.setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
|
|
285
|
+
|
|
286
|
+
# Activation button
|
|
287
|
+
push_buttons["activation"] = QPushButton(name)
|
|
288
|
+
push_buttons["activation"].setObjectName('activation')
|
|
289
|
+
push_buttons["activation"].setCheckable(True)
|
|
290
|
+
push_buttons["activation"].setChecked(True)
|
|
291
|
+
push_buttons["activation"].clicked.connect(lambda: self.view.controller.select_label(label_id))
|
|
292
|
+
new_layer_label_bar_layout.addWidget(push_buttons["activation"])
|
|
293
|
+
separator = QSeparator1()
|
|
294
|
+
self.view.label_bar_layout.addWidget(separator)
|
|
295
|
+
|
|
296
|
+
# The others buttons
|
|
297
|
+
for button in self.view.config["label_bar"]["layer"]:
|
|
298
|
+
type_name_button = button["name"]
|
|
299
|
+
|
|
300
|
+
push_buttons[type_name_button] = QPushButton()
|
|
301
|
+
push_buttons[type_name_button].setObjectName(button["name"])
|
|
302
|
+
push_buttons[type_name_button].setToolTip(button["tooltip"])
|
|
303
|
+
push_buttons[type_name_button].setCheckable(button["checkable"])
|
|
304
|
+
|
|
305
|
+
if type_name_button != "color":
|
|
306
|
+
icon_path = Utils.get_icon_path(button["icon"])
|
|
307
|
+
if os.path.exists(icon_path):
|
|
308
|
+
push_buttons[type_name_button].setIcon(QIcon(icon_path))
|
|
309
|
+
push_buttons[type_name_button].setIconSize(QSize(*self.view.config["window_size"]["icon"]))
|
|
310
|
+
|
|
311
|
+
if type_name_button == "color":
|
|
312
|
+
push_buttons[type_name_button].setStyleSheet(Utils.color_to_stylesheet(color))
|
|
313
|
+
push_buttons[type_name_button].clicked.connect(lambda: self.view.controller.color(label_id))
|
|
314
|
+
|
|
315
|
+
elif type_name_button == "visibility":
|
|
316
|
+
push_buttons[type_name_button].setChecked(True)
|
|
317
|
+
push_buttons[type_name_button].clicked.connect(lambda: self.view.controller.visibility(label_id))
|
|
318
|
+
|
|
319
|
+
elif type_name_button == "label_setting":
|
|
320
|
+
push_buttons[type_name_button].clicked.connect(lambda: self.view.controller.label_setting(label_id))
|
|
321
|
+
|
|
322
|
+
elif type_name_button == "remove_label":
|
|
323
|
+
push_buttons[type_name_button].clicked.connect(lambda: self.view.controller.remove_label(label_id))
|
|
324
|
+
|
|
325
|
+
new_layer_label_bar_layout.addWidget(push_buttons[type_name_button])
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
self.view.label_bar_layout.addWidget(new_layer_label_bar_container)
|
|
329
|
+
|
|
330
|
+
self.view.buttons_label_bar_temporary[label_id] = push_buttons # Usefull to control all buttons :)
|
|
331
|
+
self.view.container_label_bar_temporary[label_id] = (new_layer_label_bar_container, separator) # Usefull to delete these qwidget :)
|
|
332
|
+
|
|
333
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
from PyQt6.QtWidgets import QGraphicsItem
|
|
3
|
+
from PyQt6.QtGui import QColor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class QBackgroundItem(QGraphicsItem):
|
|
7
|
+
def __init__(self, rect, controller, alpha_color):
|
|
8
|
+
super().__init__()
|
|
9
|
+
self.rect = rect
|
|
10
|
+
self.controller = controller
|
|
11
|
+
self.background_color = QColor(*alpha_color)
|
|
12
|
+
|
|
13
|
+
def set_model(self, model):
|
|
14
|
+
self.model = model
|
|
15
|
+
|
|
16
|
+
def sceneEvent(self, event):
|
|
17
|
+
return self.controller.event_eater.eventFilter(event)
|
|
18
|
+
|
|
19
|
+
def boundingRect(self):
|
|
20
|
+
return self.rect
|
|
21
|
+
|
|
22
|
+
def set_background_color(self, background_color):
|
|
23
|
+
self.background_color = background_color
|
|
24
|
+
self.update()
|
|
25
|
+
|
|
26
|
+
def paint(self, painter, option, widget):
|
|
27
|
+
#painter.setPen(QColor(139,161,255))
|
|
28
|
+
#painter.drawRect(self.rect)
|
|
29
|
+
painter.fillRect(self.rect, self.background_color)
|
|
30
|
+
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
|
|
2
|
+
from PyImageLabeling.view.Builder import Builder
|
|
3
|
+
from PyImageLabeling.model.Utils import Utils
|
|
4
|
+
|
|
5
|
+
from PyQt6.QtWidgets import QListWidget, QProgressDialog, QApplication, QMainWindow, QWidget, QHBoxLayout, QListWidgetItem, QLabel, QPushButton, QGraphicsItem, QGraphicsEllipseItem
|
|
6
|
+
from PyQt6.QtGui import QPixmap, QMouseEvent, QImage, QPainter, QColor, QPen, QBrush, QCursor, QIcon, QPainterPath, QFont
|
|
7
|
+
from PyQt6.QtCore import Qt, QPoint, QPointF, QTimer, QThread, pyqtSignal, QSize, QRectF, QObject, QLineF, QDateTime
|
|
8
|
+
from functools import partial
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
class View(QMainWindow):
|
|
13
|
+
def __init__(self, controller, config):
|
|
14
|
+
|
|
15
|
+
super().__init__()
|
|
16
|
+
# Parameters
|
|
17
|
+
self.controller = controller
|
|
18
|
+
self.config = config
|
|
19
|
+
|
|
20
|
+
#Components of the view
|
|
21
|
+
self.buttons_labeling_bar = dict()
|
|
22
|
+
self.buttons_label_bar_permanent = dict()
|
|
23
|
+
self.buttons_label_bar_temporary = dict()
|
|
24
|
+
self.buttons_image_bar = dict()
|
|
25
|
+
self.buttons_file_bar = dict()
|
|
26
|
+
self.buttons_apply_cancel_bar = dict()
|
|
27
|
+
|
|
28
|
+
self.container_label_bar_temporary = dict()
|
|
29
|
+
|
|
30
|
+
self.zoomable_graphics_view = None
|
|
31
|
+
|
|
32
|
+
self.file_bar_layout = None
|
|
33
|
+
|
|
34
|
+
# Set the main properties of the view
|
|
35
|
+
self.initialize()
|
|
36
|
+
|
|
37
|
+
# Build the components of the view
|
|
38
|
+
self.builder = Builder(self)
|
|
39
|
+
self.builder.build()
|
|
40
|
+
|
|
41
|
+
self.icon_asterisk_green = QPixmap(Utils.get_icon_path("asterisk-green"))
|
|
42
|
+
self.icon_asterisk_green = self.icon_asterisk_green.scaled(QSize(*self.config["window_size"]["icon_save_marker"]), aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio)
|
|
43
|
+
|
|
44
|
+
self.icon_asterisk_red = QPixmap(Utils.get_icon_path("asterisk-red"))
|
|
45
|
+
self.icon_asterisk_red = self.icon_asterisk_red.scaled(QSize(*self.config["window_size"]["icon_save_marker"]), aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio)
|
|
46
|
+
|
|
47
|
+
self.controller.set_view(self)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Display
|
|
51
|
+
self.show()
|
|
52
|
+
|
|
53
|
+
# Here we are sure that clicked is checkable
|
|
54
|
+
def desactivate_buttons(self, clicked, list_buttons_bars):
|
|
55
|
+
for buttons_bar in list_buttons_bars:
|
|
56
|
+
for button in buttons_bar.keys():
|
|
57
|
+
# The button have not to be the same that clicked
|
|
58
|
+
# The button have to be checked
|
|
59
|
+
# The clicked button have to be checkable
|
|
60
|
+
#print("button:",button)
|
|
61
|
+
if button != clicked and buttons_bar[button].isChecked() is True:
|
|
62
|
+
buttons_bar[button].setChecked(False)
|
|
63
|
+
if button == clicked:
|
|
64
|
+
buttons_bar[button].setChecked(True)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def update_labeling_buttons(self, labeling_mode):
|
|
69
|
+
print("labeling_mode", labeling_mode)
|
|
70
|
+
category_key_selected = None
|
|
71
|
+
for category_key in self.config["labeling_bar"].keys():
|
|
72
|
+
category_name = self.config["labeling_bar"][category_key]["name_view"]
|
|
73
|
+
if category_name == labeling_mode:
|
|
74
|
+
category_key_selected = category_key
|
|
75
|
+
if category_key_selected is None:
|
|
76
|
+
raise ValueError("Bad category_key in the dictionnary `self.config[labeling_bar]` for " + str(labeling_mode)+ ".")
|
|
77
|
+
|
|
78
|
+
#print("ess:", self.buttons_labeling_bar)
|
|
79
|
+
#print("Labeling Type:", category_key_selected)
|
|
80
|
+
|
|
81
|
+
for button_key in self.buttons_labeling_bar.keys():
|
|
82
|
+
self.buttons_labeling_bar[button_key].setEnabled(False)
|
|
83
|
+
|
|
84
|
+
for config_buttons in self.config["labeling_bar"][category_key_selected]["buttons"]:
|
|
85
|
+
name = config_buttons["name"]
|
|
86
|
+
self.buttons_labeling_bar[name].setEnabled(True)
|
|
87
|
+
if name+"_setting" in self.buttons_labeling_bar.keys():
|
|
88
|
+
self.buttons_labeling_bar[name+"_setting"].setEnabled(True)
|
|
89
|
+
|
|
90
|
+
for config_buttons in self.config["labeling_bar"]["edit"]["buttons"]:
|
|
91
|
+
name = config_buttons["name"]
|
|
92
|
+
self.buttons_labeling_bar[name].setEnabled(True)
|
|
93
|
+
if name+"_setting" in self.buttons_labeling_bar.keys():
|
|
94
|
+
self.buttons_labeling_bar[name+"_setting"].setEnabled(True)
|
|
95
|
+
|
|
96
|
+
# exit(0)
|
|
97
|
+
# buttons = self.buttons_labeling_bar
|
|
98
|
+
# pixel_tools = ["contour_filling", "paintbrush", "magic_pen"]
|
|
99
|
+
# geometric_tools = ["ellipse", "rectangle", "polygon"]
|
|
100
|
+
# for button in buttons.items():
|
|
101
|
+
# if labeling_mode == "Geometric":
|
|
102
|
+
# for button in geometric_tools:
|
|
103
|
+
# self.buttons_labeling_bar[button].setEnabled(True)
|
|
104
|
+
# for button in pixel_tools:
|
|
105
|
+
# self.buttons_labeling_bar[button].setEnabled(False)
|
|
106
|
+
# elif labeling_mode == "Pixel":
|
|
107
|
+
# for button in pixel_tools:
|
|
108
|
+
# self.buttons_labeling_bar[button].setEnabled(True)
|
|
109
|
+
# for button in geometric_tools:
|
|
110
|
+
# self.buttons_labeling_bar[button].setEnabled(False)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def file_bar_add(self, current_file_paths):
|
|
115
|
+
|
|
116
|
+
item_widgets = []
|
|
117
|
+
for file in current_file_paths:
|
|
118
|
+
|
|
119
|
+
filename = os.path.basename(file)
|
|
120
|
+
|
|
121
|
+
# Create list item
|
|
122
|
+
item = QListWidgetItem()
|
|
123
|
+
item.file_path = file
|
|
124
|
+
item.filename = filename
|
|
125
|
+
|
|
126
|
+
self.file_bar_list.addItem(item)
|
|
127
|
+
|
|
128
|
+
# Create custom widget for the item
|
|
129
|
+
item_widget = QWidget()
|
|
130
|
+
item_widget.setObjectName("file_item")
|
|
131
|
+
item_layout = QHBoxLayout(item_widget)
|
|
132
|
+
item_layout.setContentsMargins(5, 2, 5, 2)
|
|
133
|
+
|
|
134
|
+
# The save marker
|
|
135
|
+
icon_button = QLabel()
|
|
136
|
+
icon_button.setPixmap(self.icon_asterisk_green)
|
|
137
|
+
icon_button.setObjectName("save_marker")
|
|
138
|
+
self.controller.model.icon_button_files[file] = icon_button
|
|
139
|
+
|
|
140
|
+
# File name label
|
|
141
|
+
file_label = QLabel(filename)
|
|
142
|
+
file_label.setObjectName("label_files")
|
|
143
|
+
file_label.setToolTip(filename) # Full filename as tooltip
|
|
144
|
+
|
|
145
|
+
# Remove button
|
|
146
|
+
remove_button = QPushButton("×")
|
|
147
|
+
remove_button.setToolTip("Remove file")
|
|
148
|
+
remove_button.setObjectName("remove_image_button")
|
|
149
|
+
|
|
150
|
+
# Connect remove button to removal function
|
|
151
|
+
remove_button.clicked.connect(partial(self.file_bar_remove, item, self.controller.model.file_paths, self.controller.model.image_items))
|
|
152
|
+
|
|
153
|
+
item_layout.addWidget(icon_button)
|
|
154
|
+
item_layout.addWidget(file_label)
|
|
155
|
+
item_layout.addWidget(remove_button)
|
|
156
|
+
item_widgets.append((item, item_widget))
|
|
157
|
+
|
|
158
|
+
for item, item_widget in item_widgets:
|
|
159
|
+
self.file_bar_list.setItemWidget(item, item_widget)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def file_bar_remove(self, item, loaded_image_paths, image_items):
|
|
163
|
+
# Get the row of the item
|
|
164
|
+
for path in loaded_image_paths:
|
|
165
|
+
if item.filename in path:
|
|
166
|
+
loaded_image_paths.remove(path)
|
|
167
|
+
del image_items[path]
|
|
168
|
+
row = self.file_bar_list.row(item)
|
|
169
|
+
if row >= 0:
|
|
170
|
+
# Remove the item from the list
|
|
171
|
+
self.file_bar_list.takeItem(row)
|
|
172
|
+
if len(loaded_image_paths) == 0:
|
|
173
|
+
for button_name in self.buttons_file_bar:
|
|
174
|
+
if 'previous' in button_name or 'next' in button_name:
|
|
175
|
+
self.buttons_file_bar[button_name].setEnabled(False)
|
|
176
|
+
for button_names in self.buttons_image_bar:
|
|
177
|
+
self.buttons_image_bar[button_names].setEnabled(False)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
self.zoomable_graphics_view.scene.clear()
|
|
181
|
+
#self.pixmap_item = None
|
|
182
|
+
#self.zoomable_graphics_view.setSceneRect(0, 0, 0, 0)
|
|
183
|
+
#self.zoomable_graphics_view.resetTransform()
|
|
184
|
+
|
|
185
|
+
def file_bar_select(self):
|
|
186
|
+
"""Handle selection change to update styling"""
|
|
187
|
+
for i in range(self.file_bar_list.count()):
|
|
188
|
+
item = self.file_bar_list.item(i)
|
|
189
|
+
item_widget = self.file_bar_list.itemWidget(item)
|
|
190
|
+
|
|
191
|
+
if item_widget:
|
|
192
|
+
if item.isSelected():
|
|
193
|
+
# Apply selected style
|
|
194
|
+
item_widget.setObjectName("selected_file_item")
|
|
195
|
+
self.controller.select_image(item)
|
|
196
|
+
else:
|
|
197
|
+
# Apply normal style
|
|
198
|
+
item_widget.setObjectName("file_item")
|
|
199
|
+
|
|
200
|
+
# Force style update
|
|
201
|
+
item_widget.style().unpolish(item_widget)
|
|
202
|
+
item_widget.style().polish(item_widget)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def initialize(self):
|
|
207
|
+
self.setWindowTitle("PyImageLabeling")
|
|
208
|
+
self.label_properties_dialogs = []
|
|
209
|
+
# Get screen information
|
|
210
|
+
self.screen = QApplication.primaryScreen()
|
|
211
|
+
|
|
212
|
+
self.screen_geometry = self.screen.availableGeometry()
|
|
213
|
+
|
|
214
|
+
self.screen_width = self.screen_geometry.width()
|
|
215
|
+
self.screen_height = self.screen_geometry.height()
|
|
216
|
+
|
|
217
|
+
# Calculate dynamic window size based on screen dimensions
|
|
218
|
+
self.window_width = int(self.screen_width * 0.80) # Use 85% of screen width
|
|
219
|
+
self.window_height = int(self.screen_height * 0.80) # Use 85% of screen height
|
|
220
|
+
|
|
221
|
+
self.setWindowIcon(QIcon(Utils.get_icon_path("maia_icon")))
|
|
222
|
+
self.setStyleSheet(Utils.get_style_css())
|
|
223
|
+
|
|
224
|
+
self.resize(self.window_width, self.window_height)
|
|
225
|
+
|
|
226
|
+
|