-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProjectViewer.py
284 lines (256 loc) · 12.5 KB
/
ProjectViewer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import Qt, QSize, QFileInfo
from PyQt5.QtWidgets import QFileDialog, QMainWindow, QTableWidgetItem, QDialog, QMessageBox, QLabel
from Model import Model
from ExifWindowUI import Ui_Dialog
from ImageWindowUI import Ui_MainWindow
from gps_utils import gps_map
# ExifViewer class.
class ExifViewer(QDialog):
def __init__(self, info, exif, parent=None):
super(ExifViewer, self).__init__(parent)
# get the Ui_Dialog.
self.ui = Ui_Dialog()
self.ui.setupUi(self)
# general info extracted from the current image.
self.info = info
# exif data extracted from the current image.
self.exif = exif
self.fill_table()
# fill the tables widgets with the available infos.
def fill_table(self):
gps = self.exif.pop('GPSInfo') if ('GPSInfo' in self.exif) else None
# fill this tab if general infos are available, else show a message in the window.
if self.info:
self.ui.tableWidget.setRowCount(len(self.info))
for r, tag in enumerate(self.info):
self.ui.tableWidget.setItem(r, 0, QTableWidgetItem(tag))
self.ui.tableWidget.setItem(r, 1, QTableWidgetItem(str(self.info[tag])))
else:
self.ui.tableWidget.hide()
no_info = QLabel()
no_info.setAlignment(Qt.AlignCenter)
no_info.setText(f"<h1>No Infos available for this image</h1>")
self.ui.gridLayout.addWidget(no_info, 0, 0, 1, 1)
# fill this tab if exif data are available, else show a message in the window.
if self.exif:
if gps:
self.ui.tableWidget1.setRowCount(len(self.exif) + len(gps))
for r, tag in enumerate(self.exif):
self.ui.tableWidget1.setItem(r, 0, QTableWidgetItem(tag))
self.ui.tableWidget1.setItem(r, 1, QTableWidgetItem(str(self.exif[tag])))
for r, tag in enumerate(gps):
self.ui.tableWidget1.setItem(len(self.exif) + r, 0, QTableWidgetItem(tag))
self.ui.tableWidget1.setItem(len(self.exif) + r, 1, QTableWidgetItem(str(gps[tag])))
# visualize the map present in gps tag of exif data.
gps_location = gps_map(gps)
self.ui.gridLayout2.addWidget(gps_location, 0, 0, 1, 1)
else:
self.ui.tableWidget1.setRowCount(len(self.exif))
for r, tag in enumerate(self.exif):
self.ui.tableWidget1.setItem(r, 0, QTableWidgetItem(tag))
self.ui.tableWidget1.setItem(r, 1, QTableWidgetItem(str(self.exif[tag])))
# show a message in the window due to gps is None.
no_gps = QLabel()
no_gps.setAlignment(Qt.AlignCenter)
no_gps.setText(f"<h1>No GPS map available for this image</h1>")
self.ui.gridLayout2.addWidget(no_gps, 0, 0, 1, 1)
else:
self.ui.tableWidget1.hide()
no_exif = QLabel()
no_exif.setAlignment(Qt.AlignCenter)
no_exif.setText(f"<h1>No Exif data available for this image</h1>")
no_gps = QLabel()
no_gps.setAlignment(Qt.AlignCenter)
no_gps.setText(f"<h1>No GPS map available for this image</h1>")
self.ui.gridLayout1.addWidget(no_exif, 0, 0, 1, 1)
self.ui.gridLayout2.addWidget(no_gps, 0, 0, 1, 1)
# ImageViewer class.
class ImageViewer(QMainWindow):
def __init__(self, model: Model):
super(ImageViewer, self).__init__()
# get the Ui_MainWindow.
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# get the model.
self.model = model
# current item from the loaded images list in list_widget.
self.current_item = -1
# initially hide list_widget.
self.ui.list_widget.hide()
self.show()
self.interaction()
# override resizeEvent to keep the image aspect ratio if any images is loaded.
def resizeEvent(self, ev):
if self.model.images:
self.set_aspect_ratio()
super().resizeEvent(ev)
# update the current image to visualize when Enter key is pressed in list_widget section.
def keyPressEvent(self, key_event):
if key_event.key() == Qt.Key_Return:
self.view_img()
else:
super().keyPressEvent(key_event)
# drag and drop event is implement to drop one or more images.
def dragEnterEvent(self, e):
if len(e.mimeData().urls()) > 0 and e.mimeData().urls()[0].isLocalFile():
qi = QFileInfo(e.mimeData().urls()[0].toLocalFile())
ext = qi.suffix()
if ext == 'jpg' or ext == 'jpeg' or ext == 'png' or ext == 'JPG' or ext == 'PNG':
e.accept()
else:
e.ignore()
else:
e.ignore()
def dropEvent(self, e):
if e.mimeData().hasUrls:
e.setDropAction(Qt.CopyAction)
e.accept()
for url in e.mimeData().urls():
f_name = str(url.toLocalFile())
self.load_image(f_name)
else:
e.ignore()
# right click to open the context menu and have all tools at hand.
def contextMenuEvent(self, event):
action = self.ui.context_menu.exec_(self.mapToGlobal(event.pos()))
if action == self.ui.open_context:
self.load_image()
elif action == self.ui.close_context:
self.close_img()
elif action == self.ui.close_all_context:
self.close_all_images()
elif action == self.ui.cw_rotate_context:
self.right_rotate()
elif action == self.ui.ccw_rotate_context:
self.left_rotate()
elif action == self.ui.info_context:
self.show_img_info()
elif action == self.ui.list_context:
self.toggle_img_list()
# it allows to do every interaction in the window.
def interaction(self):
self.ui.action_open.triggered.connect(self.load_image)
self.ui.ccw_rotate.triggered.connect(self.left_rotate)
self.ui.cw_rotate.triggered.connect(self.right_rotate)
self.ui.close_img.triggered.connect(self.close_img)
self.ui.close_all_img.triggered.connect(self.close_all_images)
self.ui.get_info.triggered.connect(self.show_img_info)
self.ui.side_list.triggered.connect(self.toggle_img_list)
self.ui.list_widget.itemClicked.connect(self.view_img)
# load an image from file_dialog or through drag and drop event.
def load_image(self, f_name=None):
# choose f_name from file_dialog if is None, which means if no image is dropped into the window.
if not f_name:
file_dialog = QFileDialog()
options = file_dialog.Options()
options |= file_dialog.DontUseNativeDialog
file_dialog.setGeometry(560, 290, 800, 480)
file_dialog.setWindowIcon(QtGui.QIcon("icons/application-dialog.png"))
f_name, _ = file_dialog.getOpenFileName(file_dialog, "Open File", '/home',
"Images (*.jpg *.jpeg *.png *.JPG *.PNG)", options=options)
# check if f_name is selected, else a warning message is shown.
if f_name:
if f_name not in self.model.file_names:
# load the f_name and the image in the model and display the image in the app.
self.model.file_names.append(f_name)
self.model.images.append(QtGui.QPixmap(f_name))
item = QtWidgets.QListWidgetItem(f_name.split('/')[-1])
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(f_name), QtGui.QIcon.Normal)
item.setIcon(icon)
self.ui.list_widget.addItem(item)
self.ui.list_widget.setCurrentRow(len(self.ui.list_widget) - 1)
self.ui.list_widget.show()
self.view_img()
else:
msg_box = QMessageBox()
msg_box.setIcon(QMessageBox.Warning)
msg_box.setWindowTitle('Warning')
msg_box.setWindowIcon(QtGui.QIcon('icons/exclamation.png'))
msg_box.setText("No image selected")
msg_box.exec_()
# update the image to display by selecting from list_widget and activate all tools;
# check if the current model image has already been chosen.
def view_img(self):
self.current_item = self.ui.list_widget.currentRow()
if self.model.images[self.current_item] != self.model.current_image:
self.model.set_current_img(self.current_item)
self.set_aspect_ratio()
self.set_tools_enabled()
# keep aspect ratio with a maximum size of 512 for width or height.
def set_aspect_ratio(self):
max_size = 512
self.ui.image_label.setPixmap(
self.model.current_image.scaled(
QSize(min(self.ui.image_label.width(), max_size), min(self.ui.image_label.height(), max_size)),
Qt.KeepAspectRatio, Qt.FastTransformation))
# rotate the current image to the left.
def left_rotate(self):
if self.model.current_image:
rotation = QtGui.QTransform().rotate(-90.0)
self.model.current_image = self.model.current_image.transformed(rotation, Qt.FastTransformation)
self.set_aspect_ratio()
# rotate the current image to the right.
def right_rotate(self):
if self.model.current_image:
rotation = QtGui.QTransform().rotate(90.0)
self.model.current_image = self.model.current_image.transformed(rotation, Qt.FastTransformation)
self.set_aspect_ratio()
# close and delete the current image (if any);
# the tools are disabled and the default image is set in the window if all loaded images are deleted.
def close_img(self):
if self.model.current_image:
self.model.delete_img(self.current_item)
self.ui.list_widget.takeItem(self.current_item)
# set default image if all images are deleted.
if self.model.images:
self.view_img()
else:
self.ui.image_label.setPixmap(QtGui.QPixmap("icons/default_img.PNG"))
self.set_tools_disabled()
self.ui.list_widget.hide()
# close all images (if any) and empty the images list;
# all tools are disabled and the default image is set in the window.
def close_all_images(self):
if self.model.current_image:
self.model.empty_images_list()
self.ui.list_widget.clear()
self.ui.image_label.setPixmap(QtGui.QPixmap("icons/default_img.PNG"))
self.set_tools_disabled()
self.ui.list_widget.hide()
# show the ExifViewer of the current image.
def show_img_info(self):
if self.model.current_image:
info = self.model.extract_general_info(self.model.file_names[self.current_item])
exif = self.model.extract_exif(self.model.file_names[self.current_item])
exif_viewer = ExifViewer(info, exif, self)
exif_viewer.show()
# toggle the images list.
def toggle_img_list(self):
if self.ui.list_widget.isHidden():
self.ui.list_widget.show()
else:
self.ui.list_widget.hide()
# set tools enabled.
def set_tools_enabled(self):
self.ui.ccw_rotate.setEnabled(True)
self.ui.cw_rotate.setEnabled(True)
self.ui.close_option.setEnabled(True)
self.ui.get_info.setEnabled(True)
self.ui.ccw_rotate_context.setEnabled(True)
self.ui.cw_rotate_context.setEnabled(True)
self.ui.close_context.setEnabled(True)
self.ui.close_all_context.setEnabled(True)
self.ui.info_context.setEnabled(True)
# set tools disabled.
def set_tools_disabled(self):
self.ui.ccw_rotate.setDisabled(True)
self.ui.cw_rotate.setDisabled(True)
self.ui.close_option.setDisabled(True)
self.ui.get_info.setDisabled(True)
self.ui.ccw_rotate_context.setDisabled(True)
self.ui.cw_rotate_context.setDisabled(True)
self.ui.close_context.setDisabled(True)
self.ui.close_all_context.setDisabled(True)
self.ui.info_context.setDisabled(True)