diff --git a/ImportPhotos.py b/ImportPhotos.py index bf074a1..20fa102 100644 --- a/ImportPhotos.py +++ b/ImportPhotos.py @@ -41,17 +41,23 @@ import exifread CHECK_MODULE = 'exifread' -except ModuleNotFoundError: - pass - +except: + try: + subprocess.call(['pip', 'install', 'exifread']) + CHECK_MODULE = '' + except ModuleNotFoundError: + pass try: if CHECK_MODULE == '': from PIL import Image from PIL.ExifTags import TAGS - CHECK_MODULE = 'PIL' -except ModuleNotFoundError: - pass +except: + try: + subprocess.call(['pip', 'install', 'pillow']) + CHECK_MODULE = '' + except ModuleNotFoundError: + pass FORM_CLASS, _ = uic.loadUiType(os.path.join( os.path.dirname(__file__), 'ui/impphotos.ui')) @@ -645,18 +651,18 @@ def get_geo_infos_from_photo(self, photo_path): time_ = "{:0>2}:{:0>2}:{:0>2}".format(tt[0], tt[1], tt[2]) timestamp = tags["GPS GPSDate"].values.replace(':', '-') + 'T' + time_ except: - date = '' - time_ = '' - timestamp = '' + date = None + time_ = None + timestamp = None try: if 'GPS GPSImgDirection' in tags: azimuth = float(tags["GPS GPSImgDirection"].values[0].num) / float( tags["GPS GPSImgDirection"].values[0].den) else: - azimuth = '' + azimuth = None except: - azimuth = '' + azimuth = None try: if 'GPS GPSImgDirectionRef' in tags: @@ -703,7 +709,7 @@ def get_geo_infos_from_photo(self, photo_path): with Image.open(photo_path) as img: info = img._getexif() - if info == None: + if info is None: return False for tag, value in info.items(): @@ -758,10 +764,10 @@ def get_geo_infos_from_photo(self, photo_path): azimuth = float(a['GPSInfo'][17]) else: north = '' - azimuth = '' + azimuth = None except: north = '' - azimuth = '' + azimuth = None maker = '' model = '' diff --git a/code/MouseClick.py b/code/MouseClick.py index 630f8dd..ca6a1a2 100644 --- a/code/MouseClick.py +++ b/code/MouseClick.py @@ -3,8 +3,8 @@ /*************************************************************************** ImportPhotos A QGIS plugin - Import photos jpegs - ------------------- + Import photos + last update : 04/01/2023 begin : February 2018 copyright : (C) 2019 by KIOS Research Center email : mariosmsk@gmail.com @@ -26,6 +26,7 @@ from .PhotosViewer import PhotoWindow import os.path + # Mouseclik import file class MouseClick(QgsMapTool): afterLeftClick = pyqtSignal() @@ -42,7 +43,7 @@ def __init__(self, canvas, drawSelf): def canvasPressEvent(self, event): if event.button() == 1: # sigeal : keep photo viewer on top of other windows - if self.photosDLG != None : + if self.photosDLG is not None: self.photosDLG.setWindowFlags(Qt.WindowStaysOnTopHint) self.drawSelf.refresh() @@ -50,12 +51,12 @@ def canvasMoveEvent(self, event): pass # sigeal : display photo on click instead of double-click - #def canvasReleaseEvent(self, event): + # def canvasReleaseEvent(self, event): def canvasDoubleClickEvent(self, event): pass # sigeal : display photo on click instead of double-click - #def canvasDoubleClickEvent(self, event): + # def canvasDoubleClickEvent(self, event): def canvasReleaseEvent(self, event): layers = self.canvas.layers() p = self.toMapCoordinates(event.pos()) @@ -78,16 +79,17 @@ def canvasReleaseEvent(self, event): ########## SHOW PHOTOS ############ feature = selected_features[0] self.drawSelf.featureIndex = feature.id() - activeLayerChanged = not hasattr(self.drawSelf, 'layerActive') or (self.drawSelf.layerActive != layer) + activeLayerChanged = not hasattr(self.drawSelf, 'layerActive') or ( + self.drawSelf.layerActive != layer) self.drawSelf.layerActive = layer self.drawSelf.fields = fields self.drawSelf.maxlen = len(self.drawSelf.layerActive.name()) self.drawSelf.layerActiveName = layer.name() self.drawSelf.iface.setActiveLayer(layer) - if self.drawSelf.maxlen>13: + if self.drawSelf.maxlen > 13: self.drawSelf.maxlen = 14 - self.drawSelf.layerActiveName = self.drawSelf.layerActive.name()+'...' + self.drawSelf.layerActiveName = self.drawSelf.layerActive.name() + '...' if 'PATH' in fields: imPath = feature.attributes()[feature.fieldNameIndex('Path')] @@ -100,13 +102,16 @@ def canvasReleaseEvent(self, event): if not os.path.exists(imPath): self.prj = QgsProject.instance() if self.prj.fileName() and 'RELPATH' in fields: - imPath = os.path.join(QFileInfo(self.prj.fileName()).absolutePath(), feature.attributes()[feature.fieldNameIndex('RelPath')]) + imPath = os.path.join(QFileInfo(self.prj.fileName()).absolutePath(), + feature.attributes()[feature.fieldNameIndex('RelPath')]) else: c = self.drawSelf.noImageFound() - if c: return + if c: + return except: c = self.drawSelf.noImageFound() - if c: return + if c: + return self.drawSelf.getImage = QImage(imPath) diff --git a/code/PhotosViewer.py b/code/PhotosViewer.py index 793a14e..d6cb949 100644 --- a/code/PhotosViewer.py +++ b/code/PhotosViewer.py @@ -3,8 +3,8 @@ /*************************************************************************** ImportPhotos A QGIS plugin - Import photos jpegs - ------------------- + Import photos + last update : 04/01/2023 begin : February 2018 copyright : (C) 2019 by KIOS Research Center email : mariosmsk@gmail.com @@ -20,12 +20,13 @@ """ from qgis.PyQt.QtWidgets import (QGraphicsView, QGraphicsScene, QVBoxLayout, QHBoxLayout, QWidget, - QLineEdit, QLabel, QSizePolicy, QPushButton, QFrame, QMenuBar, QAction, qApp, QFileDialog, QMessageBox) -from qgis.PyQt.QtCore import (Qt, pyqtSignal, QRectF, QRect, QSize, QCoreApplication) + QLineEdit, QLabel, QSizePolicy, QPushButton, QFrame, QMenuBar, QAction, qApp, + QFileDialog, QMessageBox) +from qgis.PyQt.QtCore import (QFileInfo, Qt, pyqtSignal, QRectF, QRect, QSize, QCoreApplication) from qgis.PyQt.QtGui import (QPainterPath, QIcon, QPixmap, QImage, QFont) import os.path -#Filtering opencv +# Filtering opencv opencv = False try: import cv2 @@ -34,6 +35,11 @@ opencv = True except: opencv = False + try: + subprocess.call(['pip', 'install', 'opencv-python']) + opencv = True + except ModuleNotFoundError: + pass class PhotosViewer(QGraphicsView): @@ -112,21 +118,20 @@ def mouseReleaseEvent(self, event): def resizeEvent(self, event): self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) - if len(self.selfwindow.allpictures) > 1: loc = self.viewport().geometry() - self.left_newloc = list(map(float, loc.getRect())) - self.left_newloc[0] = self.left_newloc[0] # x - self.left_newloc[1] = self.left_newloc[3]/2.4 # y - self.left_newloc[2] = self.left_newloc[2]/5 # width - self.left_newloc[3] = self.left_newloc[3]/5 # height + newloc = list(loc.getRect()) + self.left_newloc = newloc[:] + self.left_newloc[0] = self.left_newloc[0] # x + self.left_newloc[1] = self.left_newloc[3] / 2.4 # y + self.left_newloc[2] = self.left_newloc[2] / 5 # width + self.left_newloc[3] = self.left_newloc[3] / 5 # height self.leftClick.setGeometry(QRect(*(map(round, self.left_newloc)))) - newloc = list(map(float, loc.getRect())) - newloc[0] = newloc[2] - newloc[2]/5 # x - newloc[1] = newloc[3]/2.4 # y - newloc[2] = newloc[2]/5 # width - newloc[3] = newloc[3]/5 # height - self.rightClick.setGeometry(QRect(*(map(round, self.left_newloc)))) + newloc[0] = newloc[2] - newloc[2] / 5 # x + newloc[1] = newloc[3] / 2.4 # y + newloc[2] = newloc[2] / 5 # width + newloc[3] = newloc[3] / 5 # height + self.rightClick.setGeometry(QRect(*(map(round, newloc)))) # Fix rotate for the next photo self.rotate(-self.rotate_value) @@ -179,48 +184,49 @@ def __init__(self, drawSelf): super(PhotoWindow, self).__init__() self.drawSelf = drawSelf - ## Update for photo + # Update for photo self.allpictures = {} self.allpicturesdates = {} self.allpicturestimes = {} - self.allpicturesImpath= {} #feature id / picture path - self.allpicturesAzimuth={} - self.allpicturesName={} + self.allpicturesImpath = {} # feature id / picture path + self.allpicturesAzimuth = {} + self.allpicturesName = {} for i, f in enumerate(self.drawSelf.layerActive.getFeatures()): + attributes = f.attributes() if 'PATH' in self.drawSelf.fields: - imPath = f.attributes()[f.fieldNameIndex('Path')] - elif 'PHOTO' in self.drawSelf.fields : - imPath = f.attributes()[f.fieldNameIndex('photo')] + imPath = attributes[f.fieldNameIndex('Path')] + elif 'PHOTO' in self.drawSelf.fields: + imPath = attributes[f.fieldNameIndex('photo')] else: imPath = '' try: - dateTrue = str(f.attributes()[f.fieldNameIndex('Date')].toString('yyyy-MM-dd')) + dateTrue = str(attributes[f.fieldNameIndex('Date')].toString('yyyy-MM-dd')) except: - dateTrue = str(f.attributes()[f.fieldNameIndex('Date')]) + dateTrue = str(attributes[f.fieldNameIndex('Date')]) try: - timeTrue = str(f.attributes()[f.fieldNameIndex('Time')].toString('hh:mm:ss')) + timeTrue = str(attributes[f.fieldNameIndex('Time')].toString('hh:mm:ss')) except: - timeTrue = str(f.attributes()[f.fieldNameIndex('Time')]) + timeTrue = str(attributes[f.fieldNameIndex('Time')]) try: - name_ = f.attributes()[f.fieldNameIndex('Name')] + name_ = attributes[f.fieldNameIndex('Name')] name_ = name_[:-4] except: try: - name_ = f.attributes()[f.fieldNameIndex('filename')] + name_ = attributes[f.fieldNameIndex('filename')] except: name_ = '' if not os.path.exists(imPath): try: if self.drawSelf.prj.fileName() and 'RELPATH' in self.drawSelf.fields: - imPath = QFileInfo(prj.fileName()).absolutePath() + \ - feature.attributes()[feature.fieldNameIndex('RelPath')] + imPath = QFileInfo(self.drawSelf.prj.fileName()).absolutePath() + \ + attributes[f.fieldNameIndex('RelPath')] except: imPath = '' try: - azimuth = f.attributes()[f.fieldNameIndex('Azimuth')] + azimuth = attributes[f.fieldNameIndex('Azimuth')] except: - azimuth = '' + azimuth = None self.allpictures[f.id()] = name_ self.allpicturesdates[f.id()] = dateTrue @@ -236,7 +242,6 @@ def __init__(self, drawSelf): self.setWindowTitle('Photo') self.setWindowIcon(QIcon(':/plugins/ImportPhotos/icons/icon.png')) - menu_bar = QMenuBar(self) menu_bar.setGeometry(QRect(0, 0, 10000, 26)) @@ -267,7 +272,8 @@ def __init__(self, drawSelf): bands_menu = menu_bar.addMenu(self.tr('Bands')) self.opencv_filt_status = {'Edges': False, 'Red': False, 'Green': False, 'Blue': False, - '2DConvolution': False, 'Median': False, 'Gaussian': False, 'Gaussian Highpass': False} + '2DConvolution': False, 'Median': False, 'Gaussian': False, + 'Gaussian Highpass': False} self.edges_filter_btn = opencv_menu.addAction(self.tr('Edges Filter')) self.edges_filter_btn.setCheckable(True) self.edges_filter_btn.triggered.connect(self.edges_filter_call) @@ -304,24 +310,21 @@ def __init__(self, drawSelf): # # Add Filter buttons sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) - self.add_window_place = QLabel(self) #temporary + self.add_window_place = QLabel(self) # temporary self.add_window_place.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)) self.add_window_place.setFrameShape(QFrame.NoFrame) self.infoPhoto1 = QLabel(self) self.infoPhoto1.setSizePolicy(sizePolicy) self.infoPhoto1.setFrameShape(QFrame.Box) -# self.infoPhoto1.setAlignment(Qt.AlignCenter) self.infoPhoto2 = QLabel(self) self.infoPhoto2.setSizePolicy(sizePolicy) self.infoPhoto2.setFrameShape(QFrame.Box) -# self.infoPhoto2.setAlignment(Qt.AlignCenter) self.infoPhoto3 = QLabel(self) self.infoPhoto3.setSizePolicy(sizePolicy) self.infoPhoto3.setFrameShape(QFrame.Box) -# self.infoPhoto3.setAlignment(Qt.AlignCenter) self.extent = QPushButton(self) self.extent.setSizePolicy(sizePolicy) @@ -391,6 +394,10 @@ def __init__(self, drawSelf): HBlayout.addWidget(self.zoom_to_select) HBlayout.addWidget(self.hide_arrow) + spacelabel = QHBoxLayout() + spacelabel.addWidget(QLabel(self)) + VBlayout.addLayout(spacelabel) + VBlayout.addLayout(HBlayoutTop) VBlayout.addLayout(HBlayout2) VBlayout.addLayout(HBlayout) @@ -537,14 +544,16 @@ def saveas_call(self): self.outputPath = self.outputPath[0] if self.outputPath == '': return - self.drawSelf.getImage.save(self.outputPath+'.png') - self.showMessage(title='ImportPhotos', msg=self.tr('Save image at "')+self.outputPath+'.png'+self.tr('" succesfull.'), button='OK', icon='Info') + self.drawSelf.getImage.save(self.outputPath + '.png') + self.showMessage(title='ImportPhotos', + msg=self.tr('Save image at "') + self.outputPath + '.png' + self.tr('" succesfull.'), + button='OK', icon='Info') def showMessage(self, title, msg, button, icon): msgBox = QMessageBox() - if icon=='Warning': + if icon == 'Warning': msgBox.setIcon(QMessageBox.Warning) - if icon=='Info': + if icon == 'Info': msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle(title) msgBox.setText(msg) @@ -574,7 +583,7 @@ def hide_arrow_button(self): def leftClickButton(self): lastId = list(self.allpicturesImpath.keys())[-1] it = iter(self.allpicturesImpath) - + prevKey = lastId for key in it: if key == self.drawSelf.featureIndex: @@ -595,7 +604,7 @@ def rightClickButton(self): def updateWindow(self): imPath = self.allpicturesImpath[self.drawSelf.featureIndex] try: - if os.path.exists(imPath) == False: + if not os.path.exists(imPath): c = self.drawSelf.noImageFound() imPath = '' except: @@ -747,4 +756,3 @@ def tr(self, message): """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('PhotoWindow', message) - diff --git a/metadata.txt b/metadata.txt index ba853cd..1ae7795 100644 --- a/metadata.txt +++ b/metadata.txt @@ -11,7 +11,7 @@ name=ImportPhotos qgisMinimumVersion=2.99 qgisMaximumVersion=3.99 description=Import Photos -version=3.0.4 +version=3.0.5 author=Marios S. Kyriakou, George A. Christou, Panayiotis S. Kolios, KIOS Research and Innovation Center of Excellence (KIOS CoE) email=mariosmsk@gmail.com, george.a.christou@gmail.com, panayiotis.kolios@gmail.com @@ -23,7 +23,13 @@ repository=https://github.com/KIOS-Research/ImportPhotos/ # Recommended items: # Uncomment the following line and add your changelog: -changelog=2022-07-29 ImportPhotos 3.0.4: +changelog=2023-01-04 ImportPhotos 3.0.5: + Automatically install some modules (exifread, pillow, opencv-python) + Add label space in the window of the photo + Fix some issues with empty fields (Thanks @gaspermeister) + Fix python type error on photos viewer setGeometry (Thanks @faebebin) + Fix the error if the file in the Path field doesn't exist (Thanks @KrisRadowski, @turzik-x) + 2022-07-29 ImportPhotos 3.0.4: Fix some issues (Thanks @jfbourdon) 2021-11-05 ImportPhotos 3.0.3: Fix bug when import photos with dots in the filename