                               fname, err)
                  dropped_files.append(fname)
                  continue
  
              if img.size[0] != img.size[1]:
                  # Pad to square
                  new_img = Image.new("RGB", (thumbnail_size, thumbnail_size))
                  new_img.paste(img, ((thumbnail_size - img.size[0])//2,
                                      (thumbnail_size - img.size[1])//2))
                  img = new_img
              draw = ImageDraw.Draw(img)
              draw.rectangle(((0, 0), (thumbnail_size, thumbnail_size)), outline="#E5E5E5", width=1)
              samples.append(np.array(img))
  
          samples = np.array(samples)
          if not np.any(samples):
              logger.debug("No preview images collected.")
              return False
  
          if dropped_files:
              logger.debug("Removing dropped files: %s", dropped_files)
 
</s> remove         if dropped_files:
            logger.debug("Removing dropped files: %s", dropped_files)
            show_files = [fname for fname in show_files if fname not in dropped_files]

        self._previewcache["filenames"] = (self._previewcache["filenames"] +
                                           show_files)[-num_images:]
        cache = self._previewcache["images"]
 </s> add         self._previewcache["filenames"] = (cast(List[str], self._previewcache["filenames"]) +
                                           filenames)[-num_images:]
        cache = cast(Optional[np.ndarray], self._previewcache["images"]) </s> remove         samples = []
 </s> add         samples: List[np.ndarray] = [] </s> remove         samples = self._previewcache["images"].copy()
 </s> add         samples = cast(np.ndarray, self._previewcache["images"]).copy() </s> remove             placeholder = np.concatenate([np.expand_dims(self._previewcache["placeholder"],
                                                         0)] * remainder)
 </s> add             placeholder = np.concatenate([np.expand_dims(
                cast(np.ndarray, self._previewcache["placeholder"]), 0)] * remainder) </s> remove             self._previewcache["modified"] = max([os.path.getmtime(img) for img in retval])
 </s> add             self._previewcache["modified"] = max(os.path.getmtime(img) for img in retval)
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
                               fname, err)
                  dropped_files.append(fname)
                  continue
  
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
            samples.append(self._pad_and_border(img, thumbnail_size))

        return self._process_samples(samples,
                                     [fname for fname in show_files if fname not in dropped_files],
                                     num_images)

    def _pad_and_border(self, image: Image.Image, size: int) -> np.ndarray:
        """ Pad rectangle images to a square and draw borders

        Parameters
        ----------
        image: :class:`PIL.Image`
            The image to process
        size: int
            The size of the image as it should be displayed

        Returns
        -------
        :class:`PIL.Image`:
            The processed image
        """
        if image.size[0] != image.size[1]:
            # Pad to square
            new_img = Image.new("RGB", (size, size))
            new_img.paste(image, ((size - image.size[0]) // 2, (size - image.size[1]) // 2))
            image = new_img
        draw = ImageDraw.Draw(image)
        draw.rectangle(((0, 0), (size, size)), outline="#E5E5E5", width=1)
        retval = np.array(image)
        logger.trace("image shape: %s", retval.shape)  # type: ignore
        return retval

    def _process_samples(self,
                         samples: List[np.ndarray],
                         filenames: List[str],
                         num_images: int) -> bool:
        """ Process the latest sample images into a displayable image.

        Parameters
        ----------
        samples: list
            The list of extract/convert preview images to display
        filenames: list
            The full path to the filenames corresponding to the images
        num_images: int
            The number of images that should be displayed

        Returns
        -------
        bool
            ``True`` if samples succesfully compiled otherwise ``False``
        """
        asamples = np.array(samples)
        if not np.any(asamples):
              logger.debug("No preview images collected.")
              return False
  
          if dropped_files:
              logger.debug("Removing dropped files: %s", dropped_files)
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 Bugfix: Preview for extract in batch mode 
lib/gui/utils.py