Add GIF preview support

This commit is contained in:
Travis Abendshien
2024-06-04 13:43:30 -07:00
parent c1cd96f507
commit 3144440365
3 changed files with 71 additions and 19 deletions

View File

@@ -0,0 +1,31 @@
# Based on the implementation by eyllanesc:
# https://stackoverflow.com/questions/54230005/qmovie-with-border-radius
# Licensed under the Creative Commons CC BY-SA 4.0 License:
# https://creativecommons.org/licenses/by-sa/4.0/
# Modified for TagStudio: https://github.com/CyanVoxel/TagStudio
from PySide6.QtGui import QPixmap, QPainter, QBrush
from PySide6.QtWidgets import (
QProxyStyle,
)
class RoundedPixmapStyle(QProxyStyle):
def __init__(self, radius=8):
super().__init__()
self._radius = radius
def drawItemPixmap(self, painter, rectangle, alignment, pixmap):
painter.save()
pix = QPixmap(pixmap.size())
pix.fill("#00000000")
p = QPainter(pix)
p.setBrush(QBrush(pixmap))
p.setPen("#00000000")
p.setRenderHint(QPainter.RenderHint.Antialiasing)
p.drawRoundedRect(pixmap.rect(), self._radius, self._radius)
p.end()
super(RoundedPixmapStyle, self).drawItemPixmap(
painter, rectangle, alignment, pix
)
painter.restore()

View File

@@ -7,13 +7,12 @@ from pathlib import Path
import time
import typing
from datetime import datetime as dt
import cv2
import rawpy
from PIL import Image, UnidentifiedImageError, ImageFont
from PIL.Image import DecompressionBombError
from PySide6.QtCore import QModelIndex, Signal, Qt, QSize
from PySide6.QtGui import QResizeEvent, QAction
from PySide6.QtGui import QResizeEvent, QAction, QMovie
from PySide6.QtWidgets import (
QWidget,
QVBoxLayout,
@@ -27,7 +26,6 @@ from PySide6.QtWidgets import (
QMessageBox,
)
from humanfriendly import format_size
from src.core.enums import SettingItems, Theme
from src.core.library import Entry, ItemType, Library
from src.core.constants import (
@@ -37,6 +35,7 @@ from src.core.constants import (
TS_FOLDER_NAME,
FONT_TYPES,
)
from src.qt.helpers.rounded_pixmap_style import RoundedPixmapStyle
from src.qt.helpers.file_opener import FileOpenerLabel, FileOpenerHelper, open_file
from src.qt.modals.add_field import AddFieldModal
from src.qt.widgets.thumb_renderer import ThumbRenderer
@@ -95,9 +94,17 @@ class PreviewPanel(QWidget):
self.preview_img.setMinimumSize(*self.img_button_size)
self.preview_img.setFlat(True)
self.preview_img.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
self.preview_img.addAction(self.open_file_action)
self.preview_img.addAction(self.open_explorer_action)
self.preview_gif = QLabel()
self.preview_gif.setMinimumSize(*self.img_button_size)
self.preview_gif.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
self.preview_gif.setCursor(Qt.CursorShape.ArrowCursor)
self.preview_gif.addAction(self.open_file_action)
self.preview_gif.addAction(self.open_explorer_action)
self.preview_gif.hide()
self.preview_vid = VideoPlayer(driver)
self.preview_vid.hide()
self.thumb_renderer = ThumbRenderer()
@@ -119,6 +126,8 @@ class PreviewPanel(QWidget):
image_layout.addWidget(self.preview_img)
image_layout.setAlignment(self.preview_img, Qt.AlignmentFlag.AlignCenter)
image_layout.addWidget(self.preview_gif)
image_layout.setAlignment(self.preview_gif, Qt.AlignmentFlag.AlignCenter)
image_layout.addWidget(self.preview_vid)
image_layout.setAlignment(self.preview_vid, Qt.AlignmentFlag.AlignCenter)
self.image_container.setMinimumSize(*self.img_button_size)
@@ -399,20 +408,14 @@ class PreviewPanel(QWidget):
self.preview_vid.resizeVideo(adj_size)
self.preview_vid.setMaximumSize(adj_size)
self.preview_vid.setMinimumSize(adj_size)
# self.preview_img.setMinimumSize(adj_size)
# if self.preview_img.iconSize().toTuple()[0] < self.preview_img.size().toTuple()[0] + 10:
# if type(self.item) == Entry:
# filepath = os.path.normpath(f'{self.lib.library_dir}/{self.item.path}/{self.item.filename}')
# self.thumb_renderer.render(time.time(), filepath, self.preview_img.size().toTuple(), self.devicePixelRatio(),update_on_ratio_change=True)
# logging.info(f' Img Aspect Ratio: {self.image_ratio}')
# logging.info(f' Max Button Size: {size}')
# logging.info(f'Container Size: {(self.image_container.size().width(), self.image_container.size().height())}')
# logging.info(f'Final Button Size: {(adj_width, adj_height)}')
# logging.info(f'')
# logging.info(f' Icon Size: {self.preview_img.icon().actualSize().toTuple()}')
# logging.info(f'Button Size: {self.preview_img.size().toTuple()}')
self.preview_gif.setMaximumSize(adj_size)
self.preview_gif.setMinimumSize(adj_size)
proxy_style = RoundedPixmapStyle(radius=8)
self.preview_gif.setStyle(proxy_style)
self.preview_vid.setStyle(proxy_style)
m = self.preview_gif.movie()
if m:
m.setScaledSize(adj_size)
def place_add_field_button(self):
self.scroll_layout.addWidget(self.afb_container)
@@ -482,6 +485,7 @@ class PreviewPanel(QWidget):
self.preview_img.show()
self.preview_vid.stop()
self.preview_vid.hide()
self.preview_gif.hide()
self.selected = list(self.driver.selected)
self.add_field_button.setHidden(True)
@@ -492,6 +496,7 @@ class PreviewPanel(QWidget):
self.preview_img.show()
self.preview_vid.stop()
self.preview_vid.hide()
self.preview_gif.hide()
item: Entry = self.lib.get_entry(self.driver.selected[0][1])
# If a new selection is made, update the thumbnail and filepath.
if not self.selected or self.selected != self.driver.selected:
@@ -522,6 +527,21 @@ class PreviewPanel(QWidget):
# TODO: Do this somewhere else, this is just here temporarily.
try:
if filepath.suffix.lower() in [".gif"]:
movie = QMovie(str(filepath))
image = Image.open(str(filepath))
self.preview_gif.setMovie(movie)
self.resizeEvent(
QResizeEvent(
QSize(image.width, image.height),
QSize(image.width, image.height),
)
)
movie.start()
self.preview_img.hide()
self.preview_vid.hide()
self.preview_gif.show()
image = None
if filepath.suffix.lower() in IMAGE_TYPES:
image = Image.open(str(filepath))
@@ -601,6 +621,7 @@ class PreviewPanel(QWidget):
f"[PreviewPanel][ERROR] Couldn't Render thumbnail for {filepath} (because of {e})"
)
# TODO: Implement a clickable label to use for the GIF preview.
if self.preview_img.is_connected:
self.preview_img.clicked.disconnect()
self.preview_img.clicked.connect(

View File

@@ -258,7 +258,7 @@ class ThumbRenderer(QObject):
audio: AudioSegment = AudioSegment.from_file(
_filepath, _filepath.suffix.lower()[1:]
)
data = numpy.fromstring(audio._data, numpy.int16) # type: ignore
data = numpy.fromstring(audio._data, numpy.int16) # type: ignore
data_indices = numpy.linspace(1, len(data), num=adj_size)
BARS = adj_size // 5