コード例 #1
0
 def __init__(self, path, image_list, thumb_size=80, *arg, **kw):
     super(Image, self).__init__(*arg, **kw)
     self.path = path
     self.image_list = image_list
     self.name, ext = os.path.splitext(os.path.basename(self.path))
     self.selected = False
     self.thumb_size = thumb_size
     # read metadata
     self.metadata = Metadata(self.path)
     self.metadata.unsaved.connect(self.show_status)
     self.file_times = (os.path.getatime(self.path),
                        os.path.getmtime(self.path))
     # set file type
     self.file_type = self.metadata.get_mime_type()
     if not self.file_type:
         self.file_type = mimetypes.guess_type(self.path)[0]
     if not self.file_type:
         self.file_type = imghdr.what(self.path)
         if self.file_type:
             self.file_type = 'image/' + self.file_type
     # anything not recognised is assumed to be 'raw'
     if not self.file_type:
         self.file_type = 'image/raw'
     # sub widgets
     layout = QtWidgets.QGridLayout()
     layout.setSpacing(0)
     layout.setContentsMargins(3, 3, 3, 3)
     self.setLayout(layout)
     self.setToolTip(self.path)
     # label to display image
     self.image = QtWidgets.QLabel()
     self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
     layout.addWidget(self.image, 0, 0, 1, 2)
     # label to display file name
     self.label = QtWidgets.QLabel()
     self.label.setAlignment(Qt.AlignRight)
     scale_font(self.label, 80)
     layout.addWidget(self.label, 1, 1)
     # label to display status
     self.status = QtWidgets.QLabel()
     self.status.setAlignment(Qt.AlignLeft)
     set_symbol_font(self.status)
     scale_font(self.status, 80)
     layout.addWidget(self.status, 1, 0)
     self.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
     self.setObjectName("thumbnail")
     self.set_selected(False)
     self.show_status(False)
     self._set_thumb_size(self.thumb_size)
コード例 #2
0
 def get_file_data(self):
     if not os.path.isdir(self.root):
         return None
     file_list = []
     for root, dirs, files in os.walk(self.root):
         for name in files:
             base, ext = os.path.splitext(name)
             if ext.lower() in self.image_types:
                 file_list.append(os.path.join(root, name))
     file_data = {}
     for path in file_list:
         metadata = Metadata(path)
         timestamp = metadata.date_taken
         if not timestamp:
             timestamp = metadata.date_digitised
         if not timestamp:
             timestamp = metadata.date_modified
         if not timestamp:
             # use file date as last resort
             timestamp = datetime.fromtimestamp(os.path.getmtime(path))
         else:
             timestamp = timestamp.datetime
         name = os.path.basename(path)
         file_data[name] = {
             'camera'    : metadata.camera_model,
             'path'      : path,
             'name'      : name,
             'timestamp' : timestamp,
             }
     return file_data
コード例 #3
0
ファイル: imagelist.py プロジェクト: jim-easterbrook/Photini
 def reload_metadata(self):
     self.metadata = Metadata(self.path,
                              notify=self.show_status,
                              utf_safe=self.app.options.utf_safe)
     self.show_status(False)
     self.load_thumbnail()
     self.image_list.emit_selection()
コード例 #4
0
ファイル: imagelist.py プロジェクト: jim-easterbrook/Photini
 def open_file(self, path):
     path = Metadata.abspath(path)
     if not os.path.isfile(path):
         return
     if self.get_image(path):
         # already opened this path
         return
     image = Image(path, self, thumb_size=self.thumb_size)
     self.images.append(image)
     self.show_thumbnail(image)
コード例 #5
0
 def get_file_data(self):
     if not os.path.isdir(self.root):
         return None
     file_list = []
     for root, dirs, files in os.walk(self.root):
         # ignore special directories such as .thumbs
         dirs[:] = [x for x in dirs if x[0] != '.']
         for name in files:
             base, ext = os.path.splitext(name)
             if ext.lower() in self.image_types:
                 file_list.append(os.path.join(root, name))
     file_data = {}
     for path in file_list:
         metadata = Metadata(path)
         timestamp = metadata.date_taken
         if not timestamp:
             timestamp = metadata.date_digitised
         if not timestamp:
             timestamp = metadata.date_modified
         if not timestamp:
             # use file date as last resort
             timestamp = datetime.fromtimestamp(os.path.getmtime(path))
         else:
             timestamp = timestamp['datetime']
         sc_path = metadata.find_sidecar()
         name = os.path.basename(path)
         camera = metadata.camera_model
         if camera:
             camera = camera['model']
         file_data[name] = {
             'camera': camera,
             'path': path,
             'sc_path': sc_path,
             'name': name,
             'timestamp': timestamp,
         }
     return file_data
コード例 #6
0
 def copy_files(self, info_list, move):
     for info in info_list:
         dest_path = info['dest_path']
         dest_dir = os.path.dirname(dest_path)
         if not os.path.isdir(dest_dir):
             os.makedirs(dest_dir)
         sc_file = Metadata.find_side_car(info['path'])
         if move:
             shutil.move(info['path'], dest_path)
             if sc_file:
                 shutil.move(sc_file, dest_path + '.xmp')
         else:
             shutil.copy2(info['path'], dest_path)
             if sc_file:
                 shutil.copy2(sc_file, dest_path + '.xmp')
         yield info
コード例 #7
0
 def get_file_info(self, path):
     metadata = Metadata(path, None)
     timestamp = metadata.date_taken
     if not timestamp:
         timestamp = metadata.date_digitised
     if not timestamp:
         timestamp = metadata.date_modified
     if not timestamp:
         # use file date as last resort
         timestamp = datetime.fromtimestamp(os.path.getmtime(path))
     else:
         timestamp = timestamp.datetime
     folder, name = os.path.split(path)
     return {
         'camera': six.text_type(metadata.camera_model),
         'path': path,
         'name': name,
         'timestamp': timestamp,
     }
コード例 #8
0
ファイル: imagelist.py プロジェクト: esorensen7/Photini
 def __init__(self, path, image_list, thumb_size=80, *arg, **kw):
     super(Image, self).__init__(*arg, **kw)
     self.app = QtWidgets.QApplication.instance()
     self.path = path
     self.image_list = image_list
     self.name, ext = os.path.splitext(os.path.basename(self.path))
     self.selected = False
     self.thumb_size = thumb_size
     # read metadata
     self.metadata = Metadata(self.path,
                              notify=self.show_status,
                              utf_safe=self.app.options.utf_safe)
     self.file_times = (os.path.getatime(self.path),
                        os.path.getmtime(self.path))
     # set file type
     self.file_type = self.metadata.mime_type
     # sub widgets
     layout = QtWidgets.QGridLayout()
     layout.setSpacing(0)
     layout.setContentsMargins(3, 3, 3, 3)
     self.setLayout(layout)
     self.setToolTip(self.path)
     # label to display image
     self.image = QtWidgets.QLabel()
     self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
     layout.addWidget(self.image, 0, 0, 1, 2)
     # label to display file name
     self.label = QtWidgets.QLabel()
     self.label.setAlignment(Qt.AlignRight)
     scale_font(self.label, 80)
     layout.addWidget(self.label, 1, 1)
     # label to display status
     self.status = QtWidgets.QLabel()
     self.status.setAlignment(Qt.AlignLeft)
     set_symbol_font(self.status)
     scale_font(self.status, 80)
     layout.addWidget(self.status, 1, 0)
     self.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
     self.setObjectName("thumbnail")
     self.set_selected(False)
     self.show_status(False)
     self._set_thumb_size(self.thumb_size)
コード例 #9
0
ファイル: imagelist.py プロジェクト: madanyang/Photini
 def __init__(self, path, image_list, thumb_size=80, parent=None):
     QtGui.QFrame.__init__(self, parent)
     self.path = path
     self.image_list = image_list
     self.name = os.path.splitext(os.path.basename(self.path))[0]
     self.selected = False
     self.pixmap = None
     self.thumb_size = thumb_size
     self.metadata = Metadata(self.path)
     self.metadata.new_status.connect(self.show_status)
     layout = QtGui.QGridLayout()
     layout.setSpacing(0)
     layout.setContentsMargins(3, 3, 3, 3)
     self.setLayout(layout)
     # label to display image
     self.image = QtGui.QLabel()
     self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
     layout.addWidget(self.image, 0, 0, 1, 2)
     # label to display file name
     self.label = QtGui.QLabel(self.name)
     self.label.setAlignment(Qt.AlignRight)
     self.label.setStyleSheet("QLabel { font-size: 12px }")
     layout.addWidget(self.label, 1, 1)
     # label to display status
     self.status = QtGui.QLabel()
     self.status.setAlignment(Qt.AlignLeft)
     self.status.setSizePolicy(
         QtGui.QSizePolicy.Fixed, self.status.sizePolicy().verticalPolicy())
     self.status.setStyleSheet("QLabel { font-size: 12px }")
     self.status.setFont(QtGui.QFont("Dejavu Sans"))
     layout.addWidget(self.status, 1, 0)
     self.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Plain)
     self.setObjectName("thumbnail")
     self.set_selected(False)
     self.show_status(False)
     self._set_thumb_size(self.thumb_size)
コード例 #10
0
ファイル: uploader.py プロジェクト: Python3pkg/Photini
 def copy_metadata(self, image, path):
     # copy metadata, forcing IPTC creation
     md = Metadata(path, None)
     md.copy(image.metadata)
     md.save(True, 'none', True)
コード例 #11
0
ファイル: uploader.py プロジェクト: orensbruli/Photini
 def copy_metadata(self, image, path):
     # copy metadata
     md = Metadata.clone(path, image.metadata)
     # save metedata, forcing IPTC creation
     md.dirty = True
     md.save(if_mode=True, sc_mode='none', force_iptc=True)
コード例 #12
0
class Image(QtWidgets.QFrame):
    def __init__(self, path, image_list, thumb_size=80, *arg, **kw):
        super(Image, self).__init__(*arg, **kw)
        self.path = path
        self.image_list = image_list
        self.name, ext = os.path.splitext(os.path.basename(self.path))
        self.selected = False
        self.thumb_size = thumb_size
        # read image
        with open(self.path, 'rb') as pf:
            image_data = pf.read()
        # read metadata
        self.metadata = Metadata(
            self.path, image_data, new_status=self.show_status)
        # set file type
        ext = ext.lower()
        self.file_type = imghdr.what(self.path) or 'raw'
        if self.file_type == 'tiff' and ext not in ('.tif', '.tiff'):
            self.file_type = 'raw'
        # make 'master' thumbnail
        self.pixmap = QtGui.QPixmap()
        self.pixmap.loadFromData(image_data)
        unrotate = self.file_type == 'raw'
        if self.pixmap.isNull():
            # image read failed so attempt to use exif thumbnail
            thumb = self.metadata.get_exif_thumbnail()
            if thumb:
                self.pixmap.loadFromData(bytearray(thumb))
                unrotate = False
        if not self.pixmap.isNull():
            if max(self.pixmap.width(), self.pixmap.height()) > 450:
                # store a scaled down version of image to save memory
                self.pixmap = self.pixmap.scaled(
                    300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            if unrotate:
                # loading preview which is already re-oriented
                orientation = self.metadata.orientation
                if orientation and orientation.value > 1:
                    # need to unrotate and or unreflect image
                    transform = QtGui.QTransform()
                    if orientation.value in (3, 4):
                        transform = transform.rotate(180.0)
                    elif orientation.value in (5, 6):
                        transform = transform.rotate(-90.0)
                    elif orientation.value in (7, 8):
                        transform = transform.rotate(90.0)
                    if orientation.value in (2, 4, 5, 7):
                        transform = transform.scale(-1.0, 1.0)
                    self.pixmap = self.pixmap.transformed(transform)
        # sub widgets
        layout = QtWidgets.QGridLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)
        self.setLayout(layout)
        self.setToolTip(self.path)
        # label to display image
        self.image = QtWidgets.QLabel()
        self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        layout.addWidget(self.image, 0, 0, 1, 2)
        # label to display file name
        self.label = QtWidgets.QLabel()
        self.label.setAlignment(Qt.AlignRight)
        self.label.setStyleSheet("QLabel { font-size: 12px }")
        layout.addWidget(self.label, 1, 1)
        # label to display status
        self.status = QtWidgets.QLabel()
        self.status.setAlignment(Qt.AlignLeft)
        self.status.setStyleSheet("QLabel { font-size: 12px }")
        self.status.setFont(QtGui.QFont("Dejavu Sans"))
        if not self.status.fontInfo().exactMatch():
            # probably on Windows, try a different font
            self.status.setFont(QtGui.QFont("Segoe UI Symbol"))
        layout.addWidget(self.status, 1, 0)
        self.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
        self.setObjectName("thumbnail")
        self.set_selected(False)
        self.show_status(False)
        self._set_thumb_size(self.thumb_size)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_pos = event.pos()
        if event.modifiers() == Qt.ControlModifier:
            self.image_list.select_image(self, multiple_selection=True)
        elif event.modifiers() == Qt.ShiftModifier:
            self.image_list.select_image(self, extend_selection=True)
        elif not self.get_selected():
            # don't clear selection in case we're about to drag
            self.image_list.select_image(self)

    def mouseReleaseEvent(self, event):
        if event.modifiers() not in (Qt.ControlModifier, Qt.ShiftModifier):
            # clear any multiple selection
            self.image_list.select_image(self)

    def mouseMoveEvent(self, event):
        if not self.image_list.drag_icon:
            return
        if ((event.pos() - self.drag_start_pos).manhattanLength() <
                                    QtWidgets.QApplication.startDragDistance()):
            return
        paths = []
        for image in self.image_list.get_selected_images():
            paths.append(image.path)
        if not paths:
            return
        drag = QtGui.QDrag(self)
        # construct icon
        count = min(len(paths), 8)
        src_icon = self.image_list.drag_icon
        src_w = src_icon.width()
        src_h = src_icon.height()
        margin = (count - 1) * 4
        if count == 1:
            icon = src_icon
        else:
            icon = QtGui.QPixmap(src_w + margin, src_h + margin)
            icon.fill(Qt.transparent)
            with QtGui.QPainter(icon) as paint:
                for i in range(count):
                    paint.drawPixmap(
                        QtCore.QPoint(margin - (i * 4), i * 4), src_icon)
        drag.setPixmap(icon)
        if src_h == src_w:
            # round marker used in Bing maps version 8
            drag.setHotSpot(QtCore.QPoint(src_w // 2, (src_h // 2) + margin))
        else:
            drag.setHotSpot(QtCore.QPoint(src_w // 2, src_h + margin))
        mimeData = QtCore.QMimeData()
        mimeData.setData(DRAG_MIMETYPE, repr(paths).encode('utf-8'))
        drag.setMimeData(mimeData)
        dropAction = drag.exec_(Qt.CopyAction)

    def mouseDoubleClickEvent(self, event):
        if sys.platform.startswith('linux'):
            subprocess.call(['xdg-open', self.path])
        elif sys.platform.startswith('darwin'):
            subprocess.call(['open', self.path])
        elif sys.platform.startswith('win'):
            subprocess.call(['start', self.path], shell=True)

    def show_status(self, changed):
        status = ''
        # set 'geotagged' status
        if self.metadata.latlong:
            status += six.unichr(0x2690)
        # set 'unsaved' status
        if changed:
            status += six.unichr(0x26A1)
        self.status.setText(status)
        self._elide_name()
        if changed:
            self.image_list.new_metadata.emit(True)

    def _elide_name(self):
        self.status.adjustSize()
        elided_name = self.label.fontMetrics().elidedText(
            self.name, Qt.ElideLeft, self.thumb_size - self.status.width())
        self.label.setText(elided_name)

    def _set_thumb_size(self, thumb_size):
        self.thumb_size = thumb_size
        self.image.setFixedSize(self.thumb_size, self.thumb_size)
        self._elide_name()

    def set_thumb_size(self, thumb_size):
        self._set_thumb_size(thumb_size)
        self.load_thumbnail()

    def load_thumbnail(self):
        if self.pixmap.isNull():
            self.image.setText(self.tr('Can not\nload\nimage'))
        else:
            pixmap = self.pixmap
            orientation = self.metadata.orientation
            if orientation and orientation.value > 1:
                # need to rotate and or reflect image
                transform = QtGui.QTransform()
                if orientation.value in (3, 4):
                    transform = transform.rotate(180.0)
                elif orientation.value in (5, 6):
                    transform = transform.rotate(90.0)
                elif orientation.value in (7, 8):
                    transform = transform.rotate(-90.0)
                if orientation.value in (2, 4, 5, 7):
                    transform = transform.scale(-1.0, 1.0)
                pixmap = pixmap.transformed(transform)
            self.image.setPixmap(pixmap.scaled(
                self.thumb_size, self.thumb_size,
                Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def set_selected(self, value):
        self.selected = value
        if self.selected:
            self.setStyleSheet("#thumbnail {border: 2px solid red}")
        else:
            self.setStyleSheet("#thumbnail {border: 2px solid grey}")

    def get_selected(self):
        return self.selected
コード例 #13
0
ファイル: imagelist.py プロジェクト: madanyang/Photini
class Image(QtGui.QFrame):
    def __init__(self, path, image_list, thumb_size=80, parent=None):
        QtGui.QFrame.__init__(self, parent)
        self.path = path
        self.image_list = image_list
        self.name = os.path.splitext(os.path.basename(self.path))[0]
        self.selected = False
        self.pixmap = None
        self.thumb_size = thumb_size
        self.metadata = Metadata(self.path)
        self.metadata.new_status.connect(self.show_status)
        layout = QtGui.QGridLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)
        self.setLayout(layout)
        # label to display image
        self.image = QtGui.QLabel()
        self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        layout.addWidget(self.image, 0, 0, 1, 2)
        # label to display file name
        self.label = QtGui.QLabel(self.name)
        self.label.setAlignment(Qt.AlignRight)
        self.label.setStyleSheet("QLabel { font-size: 12px }")
        layout.addWidget(self.label, 1, 1)
        # label to display status
        self.status = QtGui.QLabel()
        self.status.setAlignment(Qt.AlignLeft)
        self.status.setSizePolicy(
            QtGui.QSizePolicy.Fixed, self.status.sizePolicy().verticalPolicy())
        self.status.setStyleSheet("QLabel { font-size: 12px }")
        self.status.setFont(QtGui.QFont("Dejavu Sans"))
        layout.addWidget(self.status, 1, 0)
        self.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Plain)
        self.setObjectName("thumbnail")
        self.set_selected(False)
        self.show_status(False)
        self._set_thumb_size(self.thumb_size)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_pos = event.pos()
        self.image_list.thumb_mouse_press(self.path, event)

    def mouseMoveEvent(self, event):
        if ((event.pos() - self.drag_start_pos).manhattanLength() <
                                    QtGui.QApplication.startDragDistance()):
            return
        drag = QtGui.QDrag(self)
        mimeData = QtCore.QMimeData()
        paths = list()
        for image in self.image_list.get_selected_images():
            paths.append(image.path)
        mimeData.setText(str(paths))
        drag.setMimeData(mimeData)
        dropAction = drag.exec_(Qt.LinkAction)

    def mouseDoubleClickEvent(self, event):
        if sys.platform.startswith('linux'):
            subprocess.call(['xdg-open', self.path])
        elif sys.platform.startswith('darwin'):
            subprocess.call(['open', self.path])
        elif sys.platform.startswith('win'):
            subprocess.call(['start', self.path], shell=True)

    @QtCore.pyqtSlot(bool)
    def show_status(self, changed):
        status = u''
        # set 'geotagged' status
        if self.metadata.has_GPS():
            status += unichr(0x2690)
        # set 'unsaved' status
        if changed:
            status += unichr(0x26A1)
        self.status.setText(status)
        if changed:
            self.image_list.new_metadata.emit(True)

    def _set_thumb_size(self, thumb_size):
        self.thumb_size = thumb_size
        self.image.setFixedSize(self.thumb_size, self.thumb_size)
        margins = self.layout().contentsMargins()
        self.setFixedWidth(self.thumb_size + margins.left() + margins.right() +
                           (self.frameWidth() * 2))

    def set_thumb_size(self, thumb_size):
        self._set_thumb_size(thumb_size)
        self.load_thumbnail()

    def load_thumbnail(self):
        result = False
        if not self.pixmap:
            result = True
            self.pixmap = QtGui.QPixmap(self.path)
            if max(self.pixmap.width(), self.pixmap.height()) > 400:
                # store a scaled down version of image to save memory
                self.pixmap = self.pixmap.scaled(
                    400, 400, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            orientation = self.metadata.get_item('orientation')
            if orientation is not None and orientation != 1:
                # need to rotate and or reflect image
                transform = QtGui.QTransform()
                if orientation in (3, 4):
                    transform = transform.rotate(180.0)
                elif orientation in (5, 6):
                    transform = transform.rotate(90.0)
                elif orientation in (7, 8):
                    transform = transform.rotate(-90.0)
                if orientation in (2, 4, 5, 7):
                    transform = transform.scale(-1.0, 1.0)
                self.pixmap = self.pixmap.transformed(transform)
        self.image.setPixmap(self.pixmap.scaled(
            self.thumb_size, self.thumb_size,
            Qt.KeepAspectRatio, Qt.SmoothTransformation))
        return result

    def set_selected(self, value):
        self.selected = value
        if self.selected:
            self.setStyleSheet("#thumbnail {border: 2px solid red}")
        else:
            self.setStyleSheet("#thumbnail {border: 2px solid grey}")

    def get_selected(self):
        return self.selected
コード例 #14
0
ファイル: imagelist.py プロジェクト: jim-easterbrook/Photini
 def __init__(self, path, image_list, thumb_size=80, *arg, **kw):
     super(Image, self).__init__(*arg, **kw)
     self.path = path
     self.image_list = image_list
     self.name, ext = os.path.splitext(os.path.basename(self.path))
     self.selected = False
     self.thumb_size = thumb_size
     # read image
     with open(self.path, 'rb') as pf:
         image_data = pf.read()
     # read metadata
     self.metadata = Metadata(
         self.path, image_data, new_status=self.show_status)
     # set file type
     ext = ext.lower()
     self.file_type = mimetypes.guess_type(self.path)[0]
     if not self.file_type:
         self.file_type = imghdr.what(self.path, image_data)
         if self.file_type:
             self.file_type = 'image/' + self.file_type
     # anything not recognised is assumed to be 'raw'
     if not self.file_type:
         self.file_type = 'image/raw'
     # make 'master' thumbnail
     self.pixmap = QtGui.QPixmap()
     self.pixmap.loadFromData(image_data)
     unrotate = self.file_type == 'image/x-dcraw'
     if self.pixmap.isNull():
         # image read failed so attempt to use exif thumbnail
         thumb = self.metadata.get_exif_thumbnail()
         if thumb:
             self.pixmap.loadFromData(bytearray(thumb))
             unrotate = False
     if not self.pixmap.isNull():
         if max(self.pixmap.width(), self.pixmap.height()) > 450:
             # store a scaled down version of image to save memory
             self.pixmap = self.pixmap.scaled(
                 300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation)
         if unrotate:
             # loading preview which is already re-oriented
             orientation = self.metadata.orientation
             if orientation and orientation.value > 1:
                 # need to unrotate and or unreflect image
                 transform = QtGui.QTransform()
                 if orientation.value in (3, 4):
                     transform = transform.rotate(180.0)
                 elif orientation.value in (5, 6):
                     transform = transform.rotate(-90.0)
                 elif orientation.value in (7, 8):
                     transform = transform.rotate(90.0)
                 if orientation.value in (2, 4, 5, 7):
                     transform = transform.scale(-1.0, 1.0)
                 self.pixmap = self.pixmap.transformed(transform)
     # sub widgets
     layout = QtWidgets.QGridLayout()
     layout.setSpacing(0)
     layout.setContentsMargins(3, 3, 3, 3)
     self.setLayout(layout)
     self.setToolTip(self.path)
     # label to display image
     self.image = QtWidgets.QLabel()
     self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
     layout.addWidget(self.image, 0, 0, 1, 2)
     # label to display file name
     self.label = QtWidgets.QLabel()
     self.label.setAlignment(Qt.AlignRight)
     self.label.setStyleSheet("QLabel { font-size: 12px }")
     layout.addWidget(self.label, 1, 1)
     # label to display status
     self.status = QtWidgets.QLabel()
     self.status.setAlignment(Qt.AlignLeft)
     self.status.setStyleSheet("QLabel { font-size: 12px }")
     self.status.setFont(QtGui.QFont("Dejavu Sans"))
     if not self.status.fontInfo().exactMatch():
         # probably on Windows, try a different font
         self.status.setFont(QtGui.QFont("Segoe UI Symbol"))
     layout.addWidget(self.status, 1, 0)
     self.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
     self.setObjectName("thumbnail")
     self.set_selected(False)
     self.show_status(False)
     self._set_thumb_size(self.thumb_size)
コード例 #15
0
ファイル: imagelist.py プロジェクト: jim-easterbrook/Photini
class Image(QtWidgets.QFrame):
    def __init__(self, path, image_list, thumb_size=80, *arg, **kw):
        super(Image, self).__init__(*arg, **kw)
        self.path = path
        self.image_list = image_list
        self.name, ext = os.path.splitext(os.path.basename(self.path))
        self.selected = False
        self.thumb_size = thumb_size
        # read image
        with open(self.path, 'rb') as pf:
            image_data = pf.read()
        # read metadata
        self.metadata = Metadata(
            self.path, image_data, new_status=self.show_status)
        # set file type
        ext = ext.lower()
        self.file_type = mimetypes.guess_type(self.path)[0]
        if not self.file_type:
            self.file_type = imghdr.what(self.path, image_data)
            if self.file_type:
                self.file_type = 'image/' + self.file_type
        # anything not recognised is assumed to be 'raw'
        if not self.file_type:
            self.file_type = 'image/raw'
        # make 'master' thumbnail
        self.pixmap = QtGui.QPixmap()
        self.pixmap.loadFromData(image_data)
        unrotate = self.file_type == 'image/x-dcraw'
        if self.pixmap.isNull():
            # image read failed so attempt to use exif thumbnail
            thumb = self.metadata.get_exif_thumbnail()
            if thumb:
                self.pixmap.loadFromData(bytearray(thumb))
                unrotate = False
        if not self.pixmap.isNull():
            if max(self.pixmap.width(), self.pixmap.height()) > 450:
                # store a scaled down version of image to save memory
                self.pixmap = self.pixmap.scaled(
                    300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            if unrotate:
                # loading preview which is already re-oriented
                orientation = self.metadata.orientation
                if orientation and orientation.value > 1:
                    # need to unrotate and or unreflect image
                    transform = QtGui.QTransform()
                    if orientation.value in (3, 4):
                        transform = transform.rotate(180.0)
                    elif orientation.value in (5, 6):
                        transform = transform.rotate(-90.0)
                    elif orientation.value in (7, 8):
                        transform = transform.rotate(90.0)
                    if orientation.value in (2, 4, 5, 7):
                        transform = transform.scale(-1.0, 1.0)
                    self.pixmap = self.pixmap.transformed(transform)
        # sub widgets
        layout = QtWidgets.QGridLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)
        self.setLayout(layout)
        self.setToolTip(self.path)
        # label to display image
        self.image = QtWidgets.QLabel()
        self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        layout.addWidget(self.image, 0, 0, 1, 2)
        # label to display file name
        self.label = QtWidgets.QLabel()
        self.label.setAlignment(Qt.AlignRight)
        self.label.setStyleSheet("QLabel { font-size: 12px }")
        layout.addWidget(self.label, 1, 1)
        # label to display status
        self.status = QtWidgets.QLabel()
        self.status.setAlignment(Qt.AlignLeft)
        self.status.setStyleSheet("QLabel { font-size: 12px }")
        self.status.setFont(QtGui.QFont("Dejavu Sans"))
        if not self.status.fontInfo().exactMatch():
            # probably on Windows, try a different font
            self.status.setFont(QtGui.QFont("Segoe UI Symbol"))
        layout.addWidget(self.status, 1, 0)
        self.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
        self.setObjectName("thumbnail")
        self.set_selected(False)
        self.show_status(False)
        self._set_thumb_size(self.thumb_size)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_pos = event.pos()
        if event.modifiers() == Qt.ControlModifier:
            self.image_list.select_image(self, multiple_selection=True)
        elif event.modifiers() == Qt.ShiftModifier:
            self.image_list.select_image(self, extend_selection=True)
        elif not self.get_selected():
            # don't clear selection in case we're about to drag
            self.image_list.select_image(self)

    def mouseReleaseEvent(self, event):
        if event.modifiers() not in (Qt.ControlModifier, Qt.ShiftModifier):
            # clear any multiple selection
            self.image_list.select_image(self)

    def mouseMoveEvent(self, event):
        if not self.image_list.drag_icon:
            return
        if ((event.pos() - self.drag_start_pos).manhattanLength() <
                                    QtWidgets.QApplication.startDragDistance()):
            return
        paths = []
        for image in self.image_list.get_selected_images():
            paths.append(image.path)
        if not paths:
            return
        drag = QtGui.QDrag(self)
        # construct icon
        count = min(len(paths), 8)
        src_icon = self.image_list.drag_icon
        src_w = src_icon.width()
        src_h = src_icon.height()
        margin = (count - 1) * 4
        if count == 1:
            icon = src_icon
        else:
            icon = QtGui.QPixmap(src_w + margin, src_h + margin)
            icon.fill(Qt.transparent)
            with QtGui.QPainter(icon) as paint:
                for i in range(count):
                    paint.drawPixmap(
                        QtCore.QPoint(margin - (i * 4), i * 4), src_icon)
        drag.setPixmap(icon)
        if src_h == src_w:
            # round marker used in Bing maps version 8
            drag.setHotSpot(QtCore.QPoint(src_w // 2, (src_h // 2) + margin))
        else:
            drag.setHotSpot(QtCore.QPoint(src_w // 2, src_h + margin))
        mimeData = QtCore.QMimeData()
        mimeData.setData(DRAG_MIMETYPE, repr(paths).encode('utf-8'))
        drag.setMimeData(mimeData)
        dropAction = drag.exec_(Qt.CopyAction)

    def mouseDoubleClickEvent(self, event):
        webbrowser.open(self.path)

    def show_status(self, changed):
        status = ''
        # set 'geotagged' status
        if self.metadata.latlong:
            status += six.unichr(0x2690)
        # set 'unsaved' status
        if changed:
            status += six.unichr(0x26A1)
        self.status.setText(status)
        self._elide_name()
        if changed:
            self.image_list.new_metadata.emit(True)

    def _elide_name(self):
        self.status.adjustSize()
        elided_name = self.label.fontMetrics().elidedText(
            self.name, Qt.ElideLeft, self.thumb_size - self.status.width())
        self.label.setText(elided_name)

    def _set_thumb_size(self, thumb_size):
        self.thumb_size = thumb_size
        self.image.setFixedSize(self.thumb_size, self.thumb_size)
        self._elide_name()

    def set_thumb_size(self, thumb_size):
        self._set_thumb_size(thumb_size)
        self.load_thumbnail()

    def load_thumbnail(self):
        if self.pixmap.isNull():
            self.image.setText(self.tr('Can not\ncreate\nthumbnail'))
        else:
            pixmap = self.pixmap
            orientation = self.metadata.orientation
            if orientation and orientation.value > 1:
                # need to rotate and or reflect image
                transform = QtGui.QTransform()
                if orientation.value in (3, 4):
                    transform = transform.rotate(180.0)
                elif orientation.value in (5, 6):
                    transform = transform.rotate(90.0)
                elif orientation.value in (7, 8):
                    transform = transform.rotate(-90.0)
                if orientation.value in (2, 4, 5, 7):
                    transform = transform.scale(-1.0, 1.0)
                pixmap = pixmap.transformed(transform)
            self.image.setPixmap(pixmap.scaled(
                self.thumb_size, self.thumb_size,
                Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def set_selected(self, value):
        self.selected = value
        if self.selected:
            self.setStyleSheet("#thumbnail {border: 2px solid red}")
        else:
            self.setStyleSheet("#thumbnail {border: 2px solid grey}")

    def get_selected(self):
        return self.selected
コード例 #16
0
 def diff_metadata(self):
     dialog = QtWidgets.QDialog(parent=self)
     dialog.setWindowTitle(translate('ImageList', 'Metadata differences'))
     dialog.setLayout(QtWidgets.QVBoxLayout())
     table = TableWidget()
     table.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                         QtWidgets.QSizePolicy.Expanding)
     table.setColumnCount(3)
     table.setHorizontalHeaderLabels([
         translate('ImageList', 'new value'),
         translate('ImageList', 'undo'),
         translate('ImageList', 'old value')
     ])
     labels = []
     row = 0
     undo = {}
     new_md = self.metadata
     old_md = Metadata(self.path)
     for key in ('title', 'description', 'keywords', 'rating', 'copyright',
                 'creator', 'date_taken', 'date_digitised', 'date_modified',
                 'orientation', 'lens_model', 'lens_make', 'lens_serial',
                 'lens_spec', 'focal_length', 'focal_length_35', 'aperture',
                 'latlong', 'altitude', 'location_taken', 'location_shown',
                 'thumbnail'):
         values = getattr(new_md, key), getattr(old_md, key)
         if values[0] == values[1]:
             continue
         table.setRowCount(row + 1)
         for n, value in enumerate(values):
             if not value:
                 value = ''
             elif isinstance(value, MultiString):
                 value = '\n'.join(value)
             else:
                 value = six.text_type(value)
             item = QtWidgets.QTableWidgetItem(value)
             table.setItem(row, n * 2, item)
         undo[key] = QtWidgets.QTableWidgetItem()
         undo[key].setFlags(undo[key].flags() | Qt.ItemIsUserCheckable)
         undo[key].setCheckState(False)
         table.setItem(row, 1, undo[key])
         labels.append(key)
         row += 1
     table.setVerticalHeaderLabels(labels)
     table.resizeColumnsToContents()
     table.resizeRowsToContents()
     dialog.layout().addWidget(table)
     button_box = QtWidgets.QDialogButtonBox(
         QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
     button_box.accepted.connect(dialog.accept)
     button_box.rejected.connect(dialog.reject)
     dialog.layout().addWidget(button_box)
     if dialog.exec_() != QtWidgets.QDialog.Accepted:
         return
     changed = False
     dirty = False
     for key, widget in undo.items():
         if widget.checkState() == Qt.Checked:
             setattr(new_md, key, getattr(old_md, key))
             changed = True
         else:
             dirty = True
     if not dirty:
         self.reload_metadata()
     elif changed:
         self.image_list.emit_selection()
コード例 #17
0
 def reload_metadata(self):
     self.metadata = Metadata(self.path)
     self.metadata.unsaved.connect(self.show_status)
     self.show_status(False)
     self.load_thumbnail()
     self.image_list.emit_selection()
コード例 #18
0
ファイル: imagelist.py プロジェクト: esorensen7/Photini
 def diff_selected_metadata(self):
     dialog = QtWidgets.QDialog(parent=self)
     dialog.setLayout(QtWidgets.QVBoxLayout())
     dialog.setFixedSize(min(800,
                             self.window().width()),
                         min(400,
                             self.window().height()))
     table = QtWidgets.QTableWidget()
     table.setColumnCount(3)
     table.setHorizontalHeaderLabels([
         translate('ImageList', 'new value'),
         translate('ImageList', 'undo'),
         translate('ImageList', 'old value')
     ])
     table.horizontalHeader().setSectionResizeMode(
         0, QtWidgets.QHeaderView.Stretch)
     table.horizontalHeader().setSectionResizeMode(
         2, QtWidgets.QHeaderView.Stretch)
     dialog.layout().addWidget(table)
     button_box = QtWidgets.QDialogButtonBox(
         QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
     button_box.accepted.connect(dialog.accept)
     button_box.rejected.connect(dialog.reject)
     dialog.layout().addWidget(button_box)
     changed = False
     position = None
     for image in self.get_selected_images():
         if not image.metadata.changed():
             continue
         dialog.setWindowTitle(
             translate('ImageList',
                       'Metadata differences: {}').format(image.name))
         labels = []
         row = 0
         undo = {}
         table.clearContents()
         new_md = image.metadata
         old_md = Metadata(image.path)
         for key in ('title', 'description', 'keywords', 'rating',
                     'copyright', 'creator', 'date_taken', 'date_digitised',
                     'date_modified', 'orientation', 'camera_model',
                     'lens_model', 'lens_spec', 'focal_length',
                     'focal_length_35', 'aperture', 'latlong', 'altitude',
                     'location_taken', 'location_shown', 'thumbnail'):
             values = getattr(new_md, key), getattr(old_md, key)
             if values[0] == values[1]:
                 continue
             values = [str(x or '') for x in values]
             table.setRowCount(row + 1)
             for n, value in enumerate(values):
                 item = QtWidgets.QTableWidgetItem(value)
                 table.setItem(row, n * 2, item)
             undo[key] = QtWidgets.QTableWidgetItem()
             undo[key].setFlags(undo[key].flags() | Qt.ItemIsUserCheckable)
             undo[key].setCheckState(Qt.Unchecked)
             table.setItem(row, 1, undo[key])
             labels.append(key)
             row += 1
         if not row:
             continue
         table.setVerticalHeaderLabels(labels)
         table.resizeColumnsToContents()
         table.resizeRowsToContents()
         if position:
             dialog.move(position)
         if dialog.exec_() != QtWidgets.QDialog.Accepted:
             return
         position = dialog.pos()
         undo_all = True
         for key, widget in undo.items():
             if widget.checkState() == Qt.Checked:
                 setattr(new_md, key, getattr(old_md, key))
                 changed = True
             else:
                 undo_all = False
         if undo_all:
             image.reload_metadata()
     if changed:
         self.emit_selection()
コード例 #19
0
 def __init__(self, path, image_list, thumb_size=80, *arg, **kw):
     super(Image, self).__init__(*arg, **kw)
     self.path = path
     self.image_list = image_list
     self.name, ext = os.path.splitext(os.path.basename(self.path))
     self.selected = False
     self.thumb_size = thumb_size
     # read image
     with open(self.path, 'rb') as pf:
         image_data = pf.read()
     # read metadata
     self.metadata = Metadata(
         self.path, image_data, new_status=self.show_status)
     # set file type
     ext = ext.lower()
     self.file_type = imghdr.what(self.path) or 'raw'
     if self.file_type == 'tiff' and ext not in ('.tif', '.tiff'):
         self.file_type = 'raw'
     # make 'master' thumbnail
     self.pixmap = QtGui.QPixmap()
     self.pixmap.loadFromData(image_data)
     unrotate = self.file_type == 'raw'
     if self.pixmap.isNull():
         # image read failed so attempt to use exif thumbnail
         thumb = self.metadata.get_exif_thumbnail()
         if thumb:
             self.pixmap.loadFromData(bytearray(thumb))
             unrotate = False
     if not self.pixmap.isNull():
         if max(self.pixmap.width(), self.pixmap.height()) > 450:
             # store a scaled down version of image to save memory
             self.pixmap = self.pixmap.scaled(
                 300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation)
         if unrotate:
             # loading preview which is already re-oriented
             orientation = self.metadata.orientation
             if orientation and orientation.value > 1:
                 # need to unrotate and or unreflect image
                 transform = QtGui.QTransform()
                 if orientation.value in (3, 4):
                     transform = transform.rotate(180.0)
                 elif orientation.value in (5, 6):
                     transform = transform.rotate(-90.0)
                 elif orientation.value in (7, 8):
                     transform = transform.rotate(90.0)
                 if orientation.value in (2, 4, 5, 7):
                     transform = transform.scale(-1.0, 1.0)
                 self.pixmap = self.pixmap.transformed(transform)
     # sub widgets
     layout = QtWidgets.QGridLayout()
     layout.setSpacing(0)
     layout.setContentsMargins(3, 3, 3, 3)
     self.setLayout(layout)
     self.setToolTip(self.path)
     # label to display image
     self.image = QtWidgets.QLabel()
     self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
     layout.addWidget(self.image, 0, 0, 1, 2)
     # label to display file name
     self.label = QtWidgets.QLabel()
     self.label.setAlignment(Qt.AlignRight)
     self.label.setStyleSheet("QLabel { font-size: 12px }")
     layout.addWidget(self.label, 1, 1)
     # label to display status
     self.status = QtWidgets.QLabel()
     self.status.setAlignment(Qt.AlignLeft)
     self.status.setStyleSheet("QLabel { font-size: 12px }")
     self.status.setFont(QtGui.QFont("Dejavu Sans"))
     if not self.status.fontInfo().exactMatch():
         # probably on Windows, try a different font
         self.status.setFont(QtGui.QFont("Segoe UI Symbol"))
     layout.addWidget(self.status, 1, 0)
     self.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
     self.setObjectName("thumbnail")
     self.set_selected(False)
     self.show_status(False)
     self._set_thumb_size(self.thumb_size)
コード例 #20
0
class Image(QtWidgets.QFrame):
    def __init__(self, path, image_list, thumb_size=80, *arg, **kw):
        super(Image, self).__init__(*arg, **kw)
        self.path = path
        self.image_list = image_list
        self.name, ext = os.path.splitext(os.path.basename(self.path))
        self.selected = False
        self.thumb_size = thumb_size
        # read metadata
        self.metadata = Metadata(self.path)
        self.metadata.unsaved.connect(self.show_status)
        self.file_times = (os.path.getatime(self.path),
                           os.path.getmtime(self.path))
        # set file type
        self.file_type = self.metadata.get_mime_type()
        if not self.file_type:
            self.file_type = mimetypes.guess_type(self.path)[0]
        if not self.file_type:
            self.file_type = imghdr.what(self.path)
            if self.file_type:
                self.file_type = 'image/' + self.file_type
        # anything not recognised is assumed to be 'raw'
        if not self.file_type:
            self.file_type = 'image/raw'
        # sub widgets
        layout = QtWidgets.QGridLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)
        self.setLayout(layout)
        self.setToolTip(self.path)
        # label to display image
        self.image = QtWidgets.QLabel()
        self.image.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        layout.addWidget(self.image, 0, 0, 1, 2)
        # label to display file name
        self.label = QtWidgets.QLabel()
        self.label.setAlignment(Qt.AlignRight)
        scale_font(self.label, 80)
        layout.addWidget(self.label, 1, 1)
        # label to display status
        self.status = QtWidgets.QLabel()
        self.status.setAlignment(Qt.AlignLeft)
        set_symbol_font(self.status)
        scale_font(self.status, 80)
        layout.addWidget(self.status, 1, 0)
        self.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
        self.setObjectName("thumbnail")
        self.set_selected(False)
        self.show_status(False)
        self._set_thumb_size(self.thumb_size)

    @QtCore.pyqtSlot()
    @catch_all
    def reload_metadata(self):
        self.metadata = Metadata(self.path)
        self.metadata.unsaved.connect(self.show_status)
        self.show_status(False)
        self.load_thumbnail()
        self.image_list.emit_selection()

    @QtCore.pyqtSlot()
    @catch_all
    def save_metadata(self):
        self.image_list._save_files(images=[self])

    @QtCore.pyqtSlot()
    @catch_all
    def diff_metadata(self):
        dialog = QtWidgets.QDialog(parent=self)
        dialog.setWindowTitle(self.tr('Metadata differences'))
        dialog.setLayout(QtWidgets.QVBoxLayout())
        table = TableWidget()
        table.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                            QtWidgets.QSizePolicy.Expanding)
        table.setColumnCount(3)
        table.setHorizontalHeaderLabels(
            [self.tr('new value'),
             self.tr('undo'),
             self.tr('old value')])
        labels = []
        row = 0
        undo = {}
        new_md = self.metadata
        old_md = Metadata(self.path)
        for key in ('title', 'description', 'keywords', 'rating', 'copyright',
                    'creator', 'date_taken', 'date_digitised', 'date_modified',
                    'orientation', 'lens_model', 'lens_make', 'lens_serial',
                    'lens_spec', 'focal_length', 'focal_length_35', 'aperture',
                    'latlong', 'location_taken', 'location_shown',
                    'thumbnail'):
            values = getattr(new_md, key), getattr(old_md, key)
            if values[0] == values[1]:
                continue
            table.setRowCount(row + 1)
            for n, value in enumerate(values):
                if not value:
                    value = ''
                elif isinstance(value, MultiString):
                    value = '\n'.join(value)
                else:
                    value = six.text_type(value)
                item = QtWidgets.QTableWidgetItem(value)
                table.setItem(row, n * 2, item)
            undo[key] = QtWidgets.QTableWidgetItem()
            undo[key].setFlags(undo[key].flags() | Qt.ItemIsUserCheckable)
            undo[key].setCheckState(False)
            table.setItem(row, 1, undo[key])
            labels.append(key)
            row += 1
        table.setVerticalHeaderLabels(labels)
        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        dialog.layout().addWidget(table)
        button_box = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
        button_box.accepted.connect(dialog.accept)
        button_box.rejected.connect(dialog.reject)
        dialog.layout().addWidget(button_box)
        if dialog.exec_() != QtWidgets.QDialog.Accepted:
            return
        changed = False
        dirty = False
        for key, widget in undo.items():
            if widget.checkState() == Qt.Checked:
                setattr(new_md, key, getattr(old_md, key))
                changed = True
            else:
                dirty = True
        if not dirty:
            self.reload_metadata()
        elif changed:
            self.image_list.emit_selection()

    def get_video_frame(self):
        if not cv2:
            return
        video = cv2.VideoCapture(self.path)
        if not video.isOpened():
            return
        OK, cv_image = video.read()
        if not OK:
            return
        height, width, channel = cv_image.shape
        fmt = QtGui.QImage.Format_RGB888
        # need to pad to 4 pixel multiple
        new_width = width - (width % -4)
        if channel == 4:
            # assume BGRA
            fmt = QtGui.QImage.Format_ARGB32
            np_image = np.empty((height, new_width, channel), dtype=np.uint8)
            np_image[:, :width, 0] = cv_image[:, :, 3]
            np_image[:, :width, 1] = cv_image[:, :, 2]
            np_image[:, :width, 2] = cv_image[:, :, 1]
            np_image[:, :width, 3] = cv_image[:, :, 0]
        elif channel == 3:
            # assume BGR
            np_image = np.empty((height, new_width, channel), dtype=np.uint8)
            np_image[:, :width, 0] = cv_image[:, :, 2]
            np_image[:, :width, 1] = cv_image[:, :, 1]
            np_image[:, :width, 2] = cv_image[:, :, 0]
        elif channel == 1:
            # assume Y
            channel = 3
            np_image = np.empty((height, new_width, channel), dtype=np.uint8)
            np_image[:, :width, 0] = cv_image[:, :, 0]
            np_image[:, :width, 1] = cv_image[:, :, 0]
            np_image[:, :width, 2] = cv_image[:, :, 0]
        else:
            return
        bpl = new_width * channel
        qt_im = QtGui.QImage(np_image.data, width, height, bpl, fmt)
        # attach np_image so it isn't deleted until qt_im is
        qt_im._data = np_image
        return qt_im

    def transform(self, pixmap, orientation, inverse=False):
        orientation = (orientation or 1) - 1
        if not orientation:
            return pixmap
        # need to rotate and or reflect image
        transform = QtGui.QTransform()
        if orientation & 0b001:
            # reflect left-right
            transform = transform.scale(-1.0, 1.0)
        if orientation & 0b010:
            transform = transform.rotate(180.0)
        if orientation & 0b100:
            # transpose horizontal & vertical
            transform = QtGui.QTransform(0, 1, 1, 0, 1, 1) * transform
        if inverse:
            transform = transform.transposed()
        return pixmap.transformed(transform)

    @QtCore.pyqtSlot()
    @catch_all
    def regenerate_thumbnail(self):
        with Busy():
            # get Qt image first
            qt_im = QtGui.QImage(self.path)
            if self.file_type.startswith('video') and qt_im.isNull():
                # use OpenCV to read first frame
                qt_im = self.get_video_frame()
            if not qt_im or qt_im.isNull():
                logger.error('Cannot read %s image data from %s',
                             self.file_type, self.path)
                return
            # reorient if required
            if self.file_type in ('image/x-canon-cr2', 'image/x-nikon-nef'):
                qt_im = self.transform(qt_im,
                                       self.metadata.orientation,
                                       inverse=True)
            w = qt_im.width()
            h = qt_im.height()
            # use Qt's scaling (not high quality) to pre-shrink very
            # large images, to avoid PIL "DecompressionBombWarning"
            if max(w, h) >= 6000:
                qt_im = qt_im.scaled(6000, 6000, Qt.KeepAspectRatio,
                                     Qt.SmoothTransformation)
                w = qt_im.width()
                h = qt_im.height()
            # DCF spec says thumbnail must be 160 x 120 so pad picture
            # to 4:3 aspect ratio
            if w >= h:
                new_h = int(0.5 + (float(w * 3) / 4.0))
                new_w = int(0.5 + (float(h * 4) / 3.0))
                if new_h > h:
                    pad = (new_h - h) // 2
                    qt_im = qt_im.copy(0, -pad, w, new_h)
                elif new_w > w:
                    pad = (new_w - w) // 2
                    qt_im = qt_im.copy(-pad, 0, new_w, h)
                w, h = 160, 120
            else:
                new_h = int(0.5 + (float(w * 4) / 3.0))
                new_w = int(0.5 + (float(h * 3) / 4.0))
                if new_w > w:
                    pad = (new_w - w) // 2
                    qt_im = qt_im.copy(-pad, 0, new_w, h)
                elif new_h > h:
                    pad = (new_h - h) // 2
                    qt_im = qt_im.copy(0, -pad, w, new_h)
                w, h = 120, 160
            fmt = 'JPEG'
            if PIL:
                # convert Qt image to PIL image
                buf = QtCore.QBuffer()
                buf.open(QtCore.QIODevice.WriteOnly)
                qt_im.save(buf, 'PPM')
                data = BytesIO(buf.data().data())
                pil_im = PIL.open(data)
                # scale PIL image
                pil_im = pil_im.resize((w, h), PIL.ANTIALIAS)
                # save image to memory
                data = BytesIO()
                pil_im.save(data, fmt)
                data = data.getvalue()
            else:
                # scale Qt image - not as good quality as PIL
                qt_im = qt_im.scaled(w, h, Qt.IgnoreAspectRatio,
                                     Qt.SmoothTransformation)
                # save image to memory
                buf = QtCore.QBuffer()
                buf.open(QtCore.QIODevice.WriteOnly)
                qt_im.save(buf, fmt)
                data = buf.data().data()
            # set thumbnail
            self.metadata.thumbnail = data, fmt, w, h
            # reload thumbnail
            self.load_thumbnail()

    @catch_all
    def contextMenuEvent(self, event):
        menu = QtWidgets.QMenu(self)
        menu.addAction(self.tr('Reload metadata'), self.reload_metadata)
        menu.addAction(self.tr('Save metadata'), self.save_metadata)
        menu.addAction(self.tr('View changes'), self.diff_metadata)
        menu.addAction(self.tr('Regenerate thumbnail'),
                       self.regenerate_thumbnail)
        action = menu.exec_(event.globalPos())

    @catch_all
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_pos = event.pos()
        if event.modifiers() == Qt.ControlModifier:
            self.image_list.select_image(self, multiple_selection=True)
        elif event.modifiers() == Qt.ShiftModifier:
            self.image_list.select_image(self, extend_selection=True)
        elif not self.get_selected():
            # don't clear selection in case we're about to drag
            self.image_list.select_image(self)

    @catch_all
    def mouseReleaseEvent(self, event):
        if event.modifiers() not in (Qt.ControlModifier, Qt.ShiftModifier):
            # clear any multiple selection
            self.image_list.select_image(self)

    @catch_all
    def mouseMoveEvent(self, event):
        if not self.image_list.drag_icon:
            return
        if ((event.pos() - self.drag_start_pos).manhattanLength() <
                QtWidgets.QApplication.startDragDistance()):
            return
        paths = []
        for image in self.image_list.get_selected_images():
            paths.append(image.path)
        if not paths:
            return
        drag = QtGui.QDrag(self)
        # construct icon
        count = min(len(paths), 8)
        src_icon = self.image_list.drag_icon
        src_w = src_icon.width()
        src_h = src_icon.height()
        margin = (count - 1) * 4
        if count == 1:
            icon = src_icon
        else:
            icon = QtGui.QPixmap(src_w + margin, src_h + margin)
            icon.fill(Qt.transparent)
            with QtGui.QPainter(icon) as paint:
                for i in range(count):
                    paint.drawPixmap(QtCore.QPoint(margin - (i * 4), i * 4),
                                     src_icon)
        drag.setPixmap(icon)
        if self.image_list.drag_hotspot:
            x, y = self.image_list.drag_hotspot
        else:
            x, y = src_w // 2, src_h
        drag.setHotSpot(QtCore.QPoint(x, y + margin))
        mimeData = QtCore.QMimeData()
        mimeData.setData(DRAG_MIMETYPE, repr(paths).encode('utf-8'))
        drag.setMimeData(mimeData)
        dropAction = drag.exec_(Qt.CopyAction)

    @catch_all
    def mouseDoubleClickEvent(self, event):
        webbrowser.open(self.path)

    @QtCore.pyqtSlot(bool)
    @catch_all
    def show_status(self, changed):
        status = ''
        # set 'geotagged' status
        if self.metadata.latlong:
            status += six.unichr(0x2690)
        # set 'unsaved' status
        if changed:
            status += six.unichr(0x26A1)
        self.status.setText(status)
        self._elide_name()
        if changed:
            self.image_list.new_metadata.emit(True)

    def _elide_name(self):
        self.status.adjustSize()
        elided_name = self.label.fontMetrics().elidedText(
            self.name, Qt.ElideLeft, self.thumb_size - self.status.width())
        self.label.setText(elided_name)

    def _set_thumb_size(self, thumb_size):
        self.thumb_size = thumb_size
        self.image.setFixedSize(self.thumb_size, self.thumb_size)
        self._elide_name()

    def set_thumb_size(self, thumb_size):
        self._set_thumb_size(thumb_size)
        self.load_thumbnail()

    def load_thumbnail(self):
        pixmap = QtGui.QPixmap()
        thumb = self.metadata.thumbnail
        if thumb:
            pixmap.loadFromData(thumb.data)
        if pixmap.isNull():
            self.image.setText(self.tr('No\nthumbnail\nin file'))
            return
        pixmap = self.transform(pixmap, self.metadata.orientation)
        self.image.setPixmap(
            pixmap.scaled(self.thumb_size, self.thumb_size, Qt.KeepAspectRatio,
                          Qt.SmoothTransformation))

    def set_selected(self, value):
        self.selected = value
        if self.selected:
            self.setStyleSheet("#thumbnail {border: 2px solid red}")
        else:
            self.setStyleSheet("#thumbnail {border: 2px solid grey}")

    def get_selected(self):
        return self.selected