feat(ui): add resizable thumbnail options

This commit is contained in:
Travis Abendshien
2024-07-19 23:43:50 -07:00
parent 3bfeb3c409
commit 086fc1e522
7 changed files with 136 additions and 29 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -66,7 +66,7 @@ class Ui_MainWindow(QMainWindow):
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
# ComboBox goup for search type and thumbnail size
# ComboBox group for search type and thumbnail size
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
@@ -83,17 +83,17 @@ class Ui_MainWindow(QMainWindow):
self.horizontalLayout_3.addWidget(self.comboBox_2)
# Thumbnail Size placeholder
self.comboBox = QComboBox(self.centralwidget)
self.comboBox.setObjectName(u"comboBox")
self.thumb_size_combobox = QComboBox(self.centralwidget)
self.thumb_size_combobox.setObjectName(u"thumbSizeComboBox")
sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(
self.comboBox.sizePolicy().hasHeightForWidth())
self.comboBox.setSizePolicy(sizePolicy)
self.comboBox.setMinimumWidth(128)
self.comboBox.setMaximumWidth(128)
self.horizontalLayout_3.addWidget(self.comboBox)
self.thumb_size_combobox.sizePolicy().hasHeightForWidth())
self.thumb_size_combobox.setSizePolicy(sizePolicy)
self.thumb_size_combobox.setMinimumWidth(128)
self.thumb_size_combobox.setMaximumWidth(352)
self.horizontalLayout_3.addWidget(self.thumb_size_combobox)
self.gridLayout.addLayout(self.horizontalLayout_3, 5, 0, 1, 1)
self.splitter = QSplitter()
@@ -212,10 +212,10 @@ class Ui_MainWindow(QMainWindow):
# Search type selector
self.comboBox_2.setItemText(0, QCoreApplication.translate("MainWindow", "And (Includes All Tags)"))
self.comboBox_2.setItemText(1, QCoreApplication.translate("MainWindow", "Or (Includes Any Tag)"))
self.comboBox.setCurrentText("")
self.thumb_size_combobox.setCurrentText("")
# Thumbnail size selector
self.comboBox.setPlaceholderText(
self.thumb_size_combobox.setPlaceholderText(
QCoreApplication.translate("MainWindow", u"Thumbnail Size", None))
# retranslateUi

View File

@@ -557,11 +557,17 @@ class QtDriver(QObject):
str(Path(__file__).parents[2] / "resources/qt/fonts/Oxanium-Bold.ttf")
)
self.thumb_sizes: list[tuple[str, int]] = [
("Extra Large Thumbnails", 256),
("Large Thumbnails", 192),
("Medium Thumbnails", 128),
("Small Thumbnails", 96),
("Mini Thumbnails", 76),
]
self.thumb_size = 128
self.max_results = 500
self.item_thumbs: list[ItemThumb] = []
self.thumb_renderers: list[ThumbRenderer] = []
self.collation_thumb_size = math.ceil(self.thumb_size * 2)
self.init_library_window()
@@ -596,23 +602,35 @@ class QtDriver(QObject):
self.shutdown()
def init_library_window(self):
# self._init_landing_page() # Taken care of inside the widget now
self._init_thumb_grid()
# TODO: Put this into its own method that copies the font file(s) into memory
# so the resource isn't being used, then store the specific size variations
# in a global dict for methods to access for different DPIs.
# adj_font_size = math.floor(12 * self.main_window.devicePixelRatio())
# self.ext_font = ImageFont.truetype(os.path.normpath(f'{Path(__file__).parents[2]}/resources/qt/fonts/Oxanium-Bold.ttf'), adj_font_size)
# Search Button
search_button: QPushButton = self.main_window.searchButton
search_button.clicked.connect(
lambda: self.filter_items(self.main_window.searchField.text())
)
# Search Field
search_field: QLineEdit = self.main_window.searchField
search_field.returnPressed.connect(
lambda: self.filter_items(self.main_window.searchField.text())
)
# Thumbnail Size ComboBox
thumb_size_combobox: QComboBox = self.main_window.thumb_size_combobox
for size in self.thumb_sizes:
thumb_size_combobox.addItem(size[0])
thumb_size_combobox.setCurrentIndex(2) # Default: Medium
thumb_size_combobox.currentIndexChanged.connect(
lambda: self.thumb_size_callback(thumb_size_combobox.currentIndex())
)
self._init_thumb_grid()
# Search Type ComboBox
search_type_selector: QComboBox = self.main_window.comboBox_2
search_type_selector.currentIndexChanged.connect(
lambda: self.set_search_type(
@@ -1099,6 +1117,30 @@ class QtDriver(QObject):
else:
self.paste_entry_fields_action.setText("&Paste Fields")
def thumb_size_callback(self, index: int):
"""
Performs actions needed when the thumbnail size selection is changed.
Args:
index (int): The index of the item_thumbs/ComboBox list to use.
"""
# Index 2 is the default (Medium)
if index < len(self.thumb_sizes) and index >= 0:
self.thumb_size = self.thumb_sizes[index][1]
else:
logging.error(
f"ERROR: Invalid thumbnail size index ({index}). Defaulting to 128px."
)
self.thumb_size = 128
self.update_thumbs()
for it in self.item_thumbs:
it.resize(self.thumb_size, self.thumb_size)
it.thumb_size = (self.thumb_size, self.thumb_size)
it.setMinimumSize(self.thumb_size, self.thumb_size)
it.setMaximumSize(self.thumb_size, self.thumb_size)
it.thumb_button.thumb_size = (self.thumb_size, self.thumb_size)
self.flow_container.layout().setSpacing(min(self.thumb_size // 10, 12))
def mouse_navigation(self, event: QMouseEvent):
# print(event.button())
if event.button() == Qt.MouseButton.ForwardButton:

View File

@@ -62,15 +62,10 @@ class ThumbRenderer(QObject):
# updatedImage = Signal(QPixmap)
# updatedSize = Signal(QSize)
thumb_mask_512: Image.Image = Image.open(
Path(__file__).parents[3] / "resources/qt/images/thumb_mask_512.png"
)
thumb_mask_512.load()
thumb_mask_hl_512: Image.Image = Image.open(
Path(__file__).parents[3] / "resources/qt/images/thumb_mask_hl_512.png"
)
thumb_mask_hl_512.load()
# Cached thumbnail elements.
# Key: Size + Pixel Ratio Tuple (Ex. (512, 512, 1.25))
thumb_masks: dict = {}
thumb_borders: dict = {}
thumb_loading_512: Image.Image = Image.open(
Path(__file__).parents[3] / "resources/qt/images/thumb_loading_512.png"
@@ -98,6 +93,76 @@ class ThumbRenderer(QObject):
math.floor(12 * font_pixel_ratio),
)
@staticmethod
def _get_mask(size: tuple[int, int], pixel_ratio: float) -> Image.Image:
"""
Returns a thumbnail mask given a size and pixel ratio.
If one is not already cached, then a new one will be rendered.
"""
item: Image.Image = ThumbRenderer.thumb_masks.get((*size, pixel_ratio))
if not item:
item = ThumbRenderer._render_mask(size, pixel_ratio)
ThumbRenderer.thumb_masks[(*size, pixel_ratio)] = item
return item
@staticmethod
def _get_border(size: tuple[int, int], pixel_ratio: float) -> Image.Image:
"""
Returns a thumbnail border given a size and pixel ratio.
If one is not already cached, then a new one will be rendered.
"""
item: Image.Image = ThumbRenderer.thumb_borders.get((*size, pixel_ratio))
if not item:
item = ThumbRenderer._render_border(size, pixel_ratio)
ThumbRenderer.thumb_borders[(*size, pixel_ratio)] = item
return item
@staticmethod
def _render_mask(size: tuple[int, int], pixel_ratio) -> Image.Image:
"""Renders a thumbnail mask."""
smooth_factor: int = math.ceil(2 * pixel_ratio)
radius_factor: int = 8
im: Image.Image = Image.new(
mode="L",
size=tuple([d * smooth_factor for d in size]), # type: ignore
color="black",
)
draw = ImageDraw.Draw(im)
draw.rounded_rectangle(
(0, 0) + tuple([d - 1 for d in im.size]),
radius=math.ceil(radius_factor * smooth_factor * pixel_ratio),
fill="white",
)
im = im.resize(
size,
resample=Image.Resampling.BILINEAR,
)
return im
@staticmethod
def _render_border(size: tuple[int, int], pixel_ratio) -> Image.Image:
"""Renders a thumbnail border."""
smooth_factor: int = math.ceil(2 * pixel_ratio)
radius_factor: int = 8
im: Image.Image = Image.new(
mode="RGBA",
size=tuple([d * smooth_factor for d in size]), # type: ignore
color="#00000000",
)
draw = ImageDraw.Draw(im)
draw.rounded_rectangle(
(0, 0) + tuple([d - 1 for d in im.size]),
radius=math.ceil(radius_factor * smooth_factor * pixel_ratio),
fill=None,
outline="white",
width=math.floor(pixel_ratio * 2),
)
im = im.resize(
size,
resample=Image.Resampling.BILINEAR,
)
return im
def render(
self,
timestamp: float,
@@ -324,11 +389,11 @@ class ThumbRenderer(QObject):
)
image = image.resize((new_x, new_y), resample=resampling_method)
if gradient:
mask: Image.Image = ThumbRenderer.thumb_mask_512.resize(
(adj_size, adj_size), resample=Image.Resampling.BILINEAR
).getchannel(3)
hl: Image.Image = ThumbRenderer.thumb_mask_hl_512.resize(
(adj_size, adj_size), resample=Image.Resampling.BILINEAR
mask: Image.Image = ThumbRenderer._get_mask(
(adj_size, adj_size), pixel_ratio
)
hl: Image.Image = ThumbRenderer._get_border(
(adj_size, adj_size), pixel_ratio
)
final = four_corner_gradient_background(image, adj_size, mask, hl)
else:
@@ -340,7 +405,7 @@ class ThumbRenderer(QObject):
)
draw = ImageDraw.Draw(rec)
draw.rounded_rectangle(
(0, 0) + rec.size,
(0, 0) + tuple([d - 1 for d in rec.size]),
(base_size[0] // 32) * scalar * pixel_ratio,
fill="red",
)