Merge pull request #2520 from twodoorcoupe/improve_processing_ui

Improve resizing options page UI
This commit is contained in:
Laurent Monin
2024-07-03 13:32:55 +02:00
committed by GitHub
10 changed files with 321 additions and 538 deletions

View File

@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
#
# Picard, the next-generation MusicBrainz tagger
#
# Copyright (C) 2024 Bob Swift
# Copyright (C) 2024 Giorgio Fontanive
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from collections import namedtuple
from enum import IntEnum
from picard.i18n import N_
class ResizeModes(IntEnum):
MAINTAIN_ASPECT_RATIO = 0,
SCALE_TO_WIDTH = 1,
SCALE_TO_HEIGHT = 2,
CROP_TO_FIT = 3,
STRETCH_TO_FIT = 4
CoverResizeMode = namedtuple('CoverResizeMode', ['mode', 'title', 'tooltip'])
COVER_RESIZE_MODES = (
# Items are entered in the order they should appear in the combo box.
# The number is the mode number stored in the settings and may be
# different from the order of appearance in the combo box. This will
# allow modes to be added or removed and re-ordered if required.
CoverResizeMode(ResizeModes.MAINTAIN_ASPECT_RATIO, N_('Maintain aspect ratio'), N_(
"<p>"
"Scale the source image so that it fits within the target dimensions."
"</p><p>"
"One of the final image dimensions may be less than the target dimension if "
"the source image and target dimensions have different aspect ratios."
"</p><p>"
"For example, a 2000x1000 image resized to target dimensions of "
"1000x1000 would result in a final image size of 1000x500."
"</p>"
)),
CoverResizeMode(ResizeModes.SCALE_TO_WIDTH, N_('Scale to width'), N_(
"<p>"
"Scale the width of the source image to the target width while keeping aspect ratio."
"</p><p>"
"For example, a 2000x1000 image resized to a target width of "
"1000 would result in a final image size of 1000x500."
"</p>"
)),
CoverResizeMode(ResizeModes.SCALE_TO_HEIGHT, N_('Scale to height'), N_(
"<p>"
"Scale the height of the source image to the target height while keeping aspect ratio."
"</p><p>"
"For example, a 1000x2000 image resized to a target height of "
"1000 would result in a final image size of 500x1000."
"</p>"
)),
CoverResizeMode(ResizeModes.CROP_TO_FIT, N_('Crop to fit'), N_(
"<p>"
"Scale the source image so that it completely fills the target dimensions "
"in both directions."
"</p><p>"
"If the source image and target dimensions have different aspect ratios"
"then there will be overflow in one direction which will be (center) cropped."
"</p><p>"
"For example, a 500x1000 image resized to target dimensions of "
"1000x1000 would first scale up to 1000x2000, then the excess height "
"would be center cropped resulting in the final image size of 1000x1000."
"</p>"
)),
CoverResizeMode(ResizeModes.STRETCH_TO_FIT, N_('Stretch to fit'), N_(
"<p>"
"Stretch the image to exactly fit the specified dimensions, "
"distorting it if necessary."
"</p><p>"
"For example, a 500x1000 image with target dimension of 1000x1000 "
"would be stretched horizontally resulting in the final image "
"size of 1000x1000."
"</p>"
)),
)
COVER_CONVERTING_FORMATS = ('JPEG', 'PNG', 'WebP', 'TIFF')

View File

@@ -16,6 +16,7 @@
# Copyright (C) 2020 RomFouq # Copyright (C) 2020 RomFouq
# Copyright (C) 2021 Gabriel Ferreira # Copyright (C) 2021 Gabriel Ferreira
# Copyright (C) 2021 Vladislav Karbovskii # Copyright (C) 2021 Vladislav Karbovskii
# Copyright (C) 2024 Giorgio Fontanive
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@@ -40,6 +41,7 @@ from picard.const import (
RELEASE_PRIMARY_GROUPS, RELEASE_PRIMARY_GROUPS,
RELEASE_SECONDARY_GROUPS, RELEASE_SECONDARY_GROUPS,
) )
from picard.const.cover_processing import ResizeModes
from picard.const.sys import ( from picard.const.sys import (
IS_MACOS, IS_MACOS,
IS_WIN, IS_WIN,
@@ -153,4 +155,6 @@ DEFAULT_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
DEFAULT_COVER_MIN_SIZE = 250 DEFAULT_COVER_MIN_SIZE = 250
DEFAULT_COVER_MAX_SIZE = 1000 DEFAULT_COVER_MAX_SIZE = 1000
DEFAULT_COVER_CONVERTING_FORMAT = "jpeg" DEFAULT_COVER_RESIZE_MODE = ResizeModes.MAINTAIN_ASPECT_RATIO
DEFAULT_COVER_CONVERTING_FORMAT = 'JPEG'

View File

@@ -67,13 +67,13 @@ def run_image_processors(data, coverartimage):
tags_image = image.copy() tags_image = image.copy()
for processor in tags_queue: for processor in tags_queue:
processor.run(tags_image, ProcessingTarget.TAGS) processor.run(tags_image, ProcessingTarget.TAGS)
tags_data = tags_image.get_result(default_format=True) tags_data = tags_image.get_result()
coverartimage.set_tags_data(tags_data) coverartimage.set_tags_data(tags_data)
if config.setting['save_images_to_files']: if config.setting['save_images_to_files']:
file_image = image.copy() file_image = image.copy()
for processor in file_queue: for processor in file_queue:
processor.run(file_image, ProcessingTarget.FILE) processor.run(file_image, ProcessingTarget.FILE)
file_data = file_image.get_result(default_format=True) file_data = file_image.get_result()
coverartimage.set_external_file_data(file_data) coverartimage.set_external_file_data(file_data)
log.debug( log.debug(
"Image processing for %s finished in %d ms", "Image processing for %s finished in %d ms",

View File

@@ -24,6 +24,7 @@ from PyQt6.QtCore import Qt
from picard import log from picard import log
from picard.config import get_config from picard.config import get_config
from picard.const.cover_processing import ResizeModes
from picard.extension_points.cover_art_processors import ( from picard.extension_points.cover_art_processors import (
ImageProcessor, ImageProcessor,
ProcessingTarget, ProcessingTarget,
@@ -35,86 +36,60 @@ class ResizeImage(ImageProcessor):
def save_to_file(self): def save_to_file(self):
config = get_config() config = get_config()
return config.setting['cover_file_scale_down'] or config.setting['cover_file_scale_up'] return config.setting['cover_file_resize']
def save_to_tags(self): def save_to_tags(self):
config = get_config() config = get_config()
return config.setting['cover_tags_scale_down'] or config.setting['cover_tags_scale_up'] return config.setting['cover_tags_resize']
def same_processing(self): def same_processing(self):
setting = get_config().setting setting = get_config().setting
tags_size = ( both_resize = setting['cover_tags_resize'] and setting['cover_file_resize']
setting['cover_tags_resize_target_width'] if setting['cover_tags_resize_use_width'] else 0, same_enlarge = setting['cover_tags_enlarge'] == setting['cover_file_enlarge']
setting['cover_tags_resize_target_height'] if setting['cover_tags_resize_use_height'] else 0 same_width = setting['cover_tags_resize_target_width'] == setting['cover_file_resize_target_width']
) same_height = setting['cover_tags_resize_target_height'] == setting['cover_file_resize_target_height']
file_size = ( same_resize_mode = setting['cover_tags_resize_mode'] == setting['cover_file_resize_mode']
setting['cover_file_resize_target_width'] if setting['cover_file_resize_use_width'] else 0, return both_resize and same_enlarge and same_width and same_height and same_resize_mode
setting['cover_file_resize_target_height'] if setting['cover_file_resize_use_height'] else 0
)
same_size = tags_size == file_size
tags_direction = (setting['cover_tags_scale_up'], setting['cover_tags_scale_down'])
file_direction = (setting['cover_file_scale_up'], setting['cover_file_scale_down'])
same_direction = tags_direction == file_direction and any(tags_direction)
tags_resize_mode = (setting['cover_tags_stretch'], setting['cover_tags_crop'])
file_resize_mode = (setting['cover_file_stretch'], setting['cover_file_crop'])
same_resize_mode = tags_resize_mode == file_resize_mode
return same_size and same_direction and same_resize_mode
def run(self, image, target): def run(self, image, target):
start_time = time.time() start_time = time.time()
config = get_config() config = get_config()
if target == ProcessingTarget.TAGS: if target == ProcessingTarget.TAGS:
scale_up = config.setting['cover_tags_scale_up'] scale_up = config.setting['cover_tags_enlarge']
scale_down = config.setting['cover_tags_scale_down']
use_width = config.setting['cover_tags_resize_use_width']
target_width = config.setting['cover_tags_resize_target_width'] target_width = config.setting['cover_tags_resize_target_width']
use_height = config.setting['cover_tags_resize_use_height']
target_height = config.setting['cover_tags_resize_target_height'] target_height = config.setting['cover_tags_resize_target_height']
stretch = config.setting['cover_tags_stretch'] resize_mode = config.setting['cover_tags_resize_mode']
crop = config.setting['cover_tags_crop']
else: else:
scale_up = config.setting['cover_file_scale_up'] scale_up = config.setting['cover_file_enlarge']
scale_down = config.setting['cover_file_scale_down']
use_width = config.setting['cover_file_resize_use_width']
target_width = config.setting['cover_file_resize_target_width'] target_width = config.setting['cover_file_resize_target_width']
use_height = config.setting['cover_file_resize_use_height']
target_height = config.setting['cover_file_resize_target_height'] target_height = config.setting['cover_file_resize_target_height']
stretch = config.setting['cover_file_stretch'] resize_mode = config.setting['cover_file_resize_mode']
crop = config.setting['cover_file_crop']
width_resize = target_width if use_width else image.info.width width_scale_factor = target_width / image.info.width
height_resize = target_height if use_height else image.info.height height_scale_factor = target_height / image.info.height
width_scale_factor = width_resize / image.info.width if resize_mode == ResizeModes.MAINTAIN_ASPECT_RATIO:
height_scale_factor = height_resize / image.info.height scale_factor = min(width_scale_factor, height_scale_factor)
use_both_dimensions = use_height and use_width elif resize_mode == ResizeModes.SCALE_TO_WIDTH:
if use_both_dimensions and not stretch: scale_factor = width_scale_factor
if crop: elif resize_mode == ResizeModes.SCALE_TO_HEIGHT:
scale_factor = max(width_scale_factor, height_scale_factor) scale_factor = height_scale_factor
else: else: # crop or stretch
scale_factor = min(width_scale_factor, height_scale_factor) scale_factor = max(width_scale_factor, height_scale_factor)
width_scale_factor = scale_factor if scale_factor == 1 or scale_factor > 1 and not scale_up:
height_scale_factor = scale_factor
if (width_scale_factor == 1 and height_scale_factor == 1
or ((width_scale_factor > 1 or height_scale_factor > 1) and not scale_up)
or ((width_scale_factor < 1 or height_scale_factor < 1) and not scale_down)):
# no resizing needed # no resizing needed
return return
qimage = image.get_result() qimage = image.get_qimage()
if stretch: new_width = image.info.width * scale_factor
scaled_image = qimage.scaled(width_resize, height_resize, Qt.AspectRatioMode.IgnoreAspectRatio) new_height = image.info.height * scale_factor
elif crop: if resize_mode == ResizeModes.STRETCH_TO_FIT:
scaled_image = qimage.scaled(width_resize, height_resize, Qt.AspectRatioMode.KeepAspectRatioByExpanding) new_width = image.info.width * width_scale_factor
cutoff_width = (scaled_image.width() - width_resize) // 2 new_height = image.info.height * height_scale_factor
cutoff_height = (scaled_image.height() - height_resize) // 2 scaled_image = qimage.scaled(int(new_width), int(new_height), Qt.AspectRatioMode.IgnoreAspectRatio)
scaled_image = scaled_image.copy(cutoff_width, cutoff_height, width_resize, height_resize) if resize_mode == ResizeModes.CROP_TO_FIT:
else: # keep aspect ratio cutoff_width = (scaled_image.width() - target_width) // 2
if use_both_dimensions: cutoff_height = (scaled_image.height() - target_height) // 2
scaled_image = qimage.scaled(width_resize, height_resize, Qt.AspectRatioMode.KeepAspectRatio) scaled_image = scaled_image.copy(cutoff_width, cutoff_height, target_width, target_height)
elif use_width:
scaled_image = qimage.scaledToWidth(width_resize)
else:
scaled_image = qimage.scaledToHeight(height_resize)
log.debug( log.debug(
"Resized cover art from %d x %d to %d x %d in %.2f ms", "Resized cover art from %d x %d to %d x %d in %.2f ms",

View File

@@ -63,12 +63,12 @@ class ProcessingImage:
else: else:
self._qimage = QImage.fromData(image) self._qimage = QImage.fromData(image)
def get_result(self, image_format=None, default_format=False, quality=90): def get_qimage(self):
return self._qimage
def get_result(self, image_format=None, quality=90):
if image_format is None: if image_format is None:
if not default_format: image_format = self.info.format
return self._qimage
else:
image_format = self.info.format
buffer = QBuffer() buffer = QBuffer()
if not self._qimage.save(buffer, image_format, quality=quality): if not self._qimage.save(buffer, image_format, quality=quality):
raise CoverArtEncodingError(f"Failed to encode into {image_format}") raise CoverArtEncodingError(f"Failed to encode into {image_format}")

View File

@@ -43,6 +43,7 @@ from picard.const.defaults import (
DEFAULT_COVER_IMAGE_FILENAME, DEFAULT_COVER_IMAGE_FILENAME,
DEFAULT_COVER_MAX_SIZE, DEFAULT_COVER_MAX_SIZE,
DEFAULT_COVER_MIN_SIZE, DEFAULT_COVER_MIN_SIZE,
DEFAULT_COVER_RESIZE_MODE,
DEFAULT_CURRENT_BROWSER_PATH, DEFAULT_CURRENT_BROWSER_PATH,
DEFAULT_DRIVES, DEFAULT_DRIVES,
DEFAULT_FPCALC_THREADS, DEFAULT_FPCALC_THREADS,
@@ -176,24 +177,18 @@ BoolOption('setting', 'save_only_one_front_image', False, title=N_("Save only a
BoolOption('setting', 'filter_cover_by_size', False) BoolOption('setting', 'filter_cover_by_size', False)
IntOption('setting', 'cover_minimum_width', DEFAULT_COVER_MIN_SIZE) IntOption('setting', 'cover_minimum_width', DEFAULT_COVER_MIN_SIZE)
IntOption('setting', 'cover_minimum_height', DEFAULT_COVER_MIN_SIZE) IntOption('setting', 'cover_minimum_height', DEFAULT_COVER_MIN_SIZE)
BoolOption('setting', 'cover_tags_scale_up', False) BoolOption('setting', 'cover_tags_enlarge', False)
BoolOption('setting', 'cover_tags_scale_down', False) BoolOption('setting', 'cover_tags_resize', False)
BoolOption('setting', 'cover_tags_resize_use_width', True)
IntOption('setting', 'cover_tags_resize_target_width', DEFAULT_COVER_MAX_SIZE) IntOption('setting', 'cover_tags_resize_target_width', DEFAULT_COVER_MAX_SIZE)
BoolOption('setting', 'cover_tags_resize_use_height', True)
IntOption('setting', 'cover_tags_resize_target_height', DEFAULT_COVER_MAX_SIZE) IntOption('setting', 'cover_tags_resize_target_height', DEFAULT_COVER_MAX_SIZE)
BoolOption('setting', 'cover_tags_stretch', False) IntOption('setting', 'cover_tags_resize_mode', DEFAULT_COVER_RESIZE_MODE)
BoolOption('setting', 'cover_tags_crop', False)
BoolOption('setting', 'cover_tags_convert_images', False) BoolOption('setting', 'cover_tags_convert_images', False)
TextOption('setting', 'cover_tags_convert_to_format', DEFAULT_COVER_CONVERTING_FORMAT) TextOption('setting', 'cover_tags_convert_to_format', DEFAULT_COVER_CONVERTING_FORMAT)
BoolOption('setting', 'cover_file_scale_up', False) BoolOption('setting', 'cover_file_enlarge', False)
BoolOption('setting', 'cover_file_scale_down', False) BoolOption('setting', 'cover_file_resize', False)
BoolOption('setting', 'cover_file_resize_use_width', True)
IntOption('setting', 'cover_file_resize_target_width', DEFAULT_COVER_MAX_SIZE) IntOption('setting', 'cover_file_resize_target_width', DEFAULT_COVER_MAX_SIZE)
BoolOption('setting', 'cover_file_resize_use_height', True)
IntOption('setting', 'cover_file_resize_target_height', DEFAULT_COVER_MAX_SIZE) IntOption('setting', 'cover_file_resize_target_height', DEFAULT_COVER_MAX_SIZE)
BoolOption('setting', 'cover_file_stretch', False) IntOption('setting', 'cover_file_resize_mode', DEFAULT_COVER_RESIZE_MODE)
BoolOption('setting', 'cover_file_crop', False)
BoolOption('setting', 'cover_file_convert_images', False) BoolOption('setting', 'cover_file_convert_images', False)
TextOption('setting', 'cover_file_convert_to_format', DEFAULT_COVER_CONVERTING_FORMAT) TextOption('setting', 'cover_file_convert_to_format', DEFAULT_COVER_CONVERTING_FORMAT)

View File

@@ -17,7 +17,7 @@ from picard.i18n import gettext as _
class Ui_CoverProcessingOptionsPage(object): class Ui_CoverProcessingOptionsPage(object):
def setupUi(self, CoverProcessingOptionsPage): def setupUi(self, CoverProcessingOptionsPage):
CoverProcessingOptionsPage.setObjectName("CoverProcessingOptionsPage") CoverProcessingOptionsPage.setObjectName("CoverProcessingOptionsPage")
CoverProcessingOptionsPage.resize(529, 467) CoverProcessingOptionsPage.resize(535, 475)
self.verticalLayout = QtWidgets.QVBoxLayout(CoverProcessingOptionsPage) self.verticalLayout = QtWidgets.QVBoxLayout(CoverProcessingOptionsPage)
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.filtering = QtWidgets.QGroupBox(parent=CoverProcessingOptionsPage) self.filtering = QtWidgets.QGroupBox(parent=CoverProcessingOptionsPage)
@@ -87,31 +87,22 @@ class Ui_CoverProcessingOptionsPage(object):
self.resizing.setObjectName("resizing") self.resizing.setObjectName("resizing")
self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.resizing) self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.resizing)
self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.horizontalLayout_7.setObjectName("horizontalLayout_7")
self.save_to_tags = QtWidgets.QGroupBox(parent=self.resizing) self.tags_scale_down = QtWidgets.QGroupBox(parent=self.resizing)
self.save_to_tags.setCheckable(False) self.tags_scale_down.setCheckable(True)
self.save_to_tags.setChecked(False) self.tags_scale_down.setChecked(False)
self.save_to_tags.setObjectName("save_to_tags")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.save_to_tags)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.tags_scale_widget = QtWidgets.QWidget(parent=self.save_to_tags)
self.tags_scale_widget.setObjectName("tags_scale_widget")
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.tags_scale_widget)
self.horizontalLayout_8.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
self.tags_scale_up = QtWidgets.QCheckBox(parent=self.tags_scale_widget)
self.tags_scale_up.setObjectName("tags_scale_up")
self.horizontalLayout_8.addWidget(self.tags_scale_up)
self.tags_scale_down = QtWidgets.QCheckBox(parent=self.tags_scale_widget)
self.tags_scale_down.setObjectName("tags_scale_down") self.tags_scale_down.setObjectName("tags_scale_down")
self.horizontalLayout_8.addWidget(self.tags_scale_down) self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tags_scale_down)
self.verticalLayout_3.addWidget(self.tags_scale_widget) self.verticalLayout_3.setObjectName("verticalLayout_3")
self.tags_resize_width_widget = QtWidgets.QWidget(parent=self.save_to_tags) self.tags_resize_mode = QtWidgets.QComboBox(parent=self.tags_scale_down)
self.tags_resize_mode.setObjectName("tags_resize_mode")
self.verticalLayout_3.addWidget(self.tags_resize_mode)
self.tags_resize_width_widget = QtWidgets.QWidget(parent=self.tags_scale_down)
self.tags_resize_width_widget.setObjectName("tags_resize_width_widget") self.tags_resize_width_widget.setObjectName("tags_resize_width_widget")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.tags_resize_width_widget) self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.tags_resize_width_widget)
self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_5.setSpacing(4) self.horizontalLayout_5.setSpacing(4)
self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.tags_resize_width_label = QtWidgets.QCheckBox(parent=self.tags_resize_width_widget) self.tags_resize_width_label = QtWidgets.QLabel(parent=self.tags_resize_width_widget)
self.tags_resize_width_label.setObjectName("tags_resize_width_label") self.tags_resize_width_label.setObjectName("tags_resize_width_label")
self.horizontalLayout_5.addWidget(self.tags_resize_width_label) self.horizontalLayout_5.addWidget(self.tags_resize_width_label)
self.tags_resize_width_value = QtWidgets.QSpinBox(parent=self.tags_resize_width_widget) self.tags_resize_width_value = QtWidgets.QSpinBox(parent=self.tags_resize_width_widget)
@@ -120,6 +111,7 @@ class Ui_CoverProcessingOptionsPage(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tags_resize_width_value.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.tags_resize_width_value.sizePolicy().hasHeightForWidth())
self.tags_resize_width_value.setSizePolicy(sizePolicy) self.tags_resize_width_value.setSizePolicy(sizePolicy)
self.tags_resize_width_value.setMinimum(1)
self.tags_resize_width_value.setMaximum(9999) self.tags_resize_width_value.setMaximum(9999)
self.tags_resize_width_value.setProperty("value", 1000) self.tags_resize_width_value.setProperty("value", 1000)
self.tags_resize_width_value.setObjectName("tags_resize_width_value") self.tags_resize_width_value.setObjectName("tags_resize_width_value")
@@ -133,13 +125,13 @@ class Ui_CoverProcessingOptionsPage(object):
self.px_label5.setObjectName("px_label5") self.px_label5.setObjectName("px_label5")
self.horizontalLayout_5.addWidget(self.px_label5) self.horizontalLayout_5.addWidget(self.px_label5)
self.verticalLayout_3.addWidget(self.tags_resize_width_widget) self.verticalLayout_3.addWidget(self.tags_resize_width_widget)
self.tags_resize_height_widget = QtWidgets.QWidget(parent=self.save_to_tags) self.tags_resize_height_widget = QtWidgets.QWidget(parent=self.tags_scale_down)
self.tags_resize_height_widget.setObjectName("tags_resize_height_widget") self.tags_resize_height_widget.setObjectName("tags_resize_height_widget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tags_resize_height_widget) self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tags_resize_height_widget)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_3.setSpacing(4) self.horizontalLayout_3.setSpacing(4)
self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.tags_resize_height_label = QtWidgets.QCheckBox(parent=self.tags_resize_height_widget) self.tags_resize_height_label = QtWidgets.QLabel(parent=self.tags_resize_height_widget)
self.tags_resize_height_label.setObjectName("tags_resize_height_label") self.tags_resize_height_label.setObjectName("tags_resize_height_label")
self.horizontalLayout_3.addWidget(self.tags_resize_height_label) self.horizontalLayout_3.addWidget(self.tags_resize_height_label)
self.tags_resize_height_value = QtWidgets.QSpinBox(parent=self.tags_resize_height_widget) self.tags_resize_height_value = QtWidgets.QSpinBox(parent=self.tags_resize_height_widget)
@@ -148,6 +140,7 @@ class Ui_CoverProcessingOptionsPage(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tags_resize_height_value.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.tags_resize_height_value.sizePolicy().hasHeightForWidth())
self.tags_resize_height_value.setSizePolicy(sizePolicy) self.tags_resize_height_value.setSizePolicy(sizePolicy)
self.tags_resize_height_value.setMinimum(1)
self.tags_resize_height_value.setMaximum(9999) self.tags_resize_height_value.setMaximum(9999)
self.tags_resize_height_value.setProperty("value", 1000) self.tags_resize_height_value.setProperty("value", 1000)
self.tags_resize_height_value.setObjectName("tags_resize_height_value") self.tags_resize_height_value.setObjectName("tags_resize_height_value")
@@ -161,49 +154,31 @@ class Ui_CoverProcessingOptionsPage(object):
self.px_label6.setObjectName("px_label6") self.px_label6.setObjectName("px_label6")
self.horizontalLayout_3.addWidget(self.px_label6) self.horizontalLayout_3.addWidget(self.px_label6)
self.verticalLayout_3.addWidget(self.tags_resize_height_widget) self.verticalLayout_3.addWidget(self.tags_resize_height_widget)
self.tags_resize_mode = QtWidgets.QWidget(parent=self.save_to_tags) self.tags_scale_up = QtWidgets.QCheckBox(parent=self.tags_scale_down)
self.tags_resize_mode.setObjectName("tags_resize_mode") self.tags_scale_up.setObjectName("tags_scale_up")
self.horizontalLayout_10 = QtWidgets.QHBoxLayout(self.tags_resize_mode) self.verticalLayout_3.addWidget(self.tags_scale_up)
self.horizontalLayout_10.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_7.addWidget(self.tags_scale_down)
self.horizontalLayout_10.setSpacing(2) self.file_scale_down = QtWidgets.QGroupBox(parent=self.resizing)
self.horizontalLayout_10.setObjectName("horizontalLayout_10") self.file_scale_down.setCheckable(True)
self.tags_keep = QtWidgets.QRadioButton(parent=self.tags_resize_mode) self.file_scale_down.setObjectName("file_scale_down")
self.tags_keep.setChecked(True) self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.file_scale_down)
self.tags_keep.setObjectName("tags_keep")
self.horizontalLayout_10.addWidget(self.tags_keep)
self.tags_crop = QtWidgets.QRadioButton(parent=self.tags_resize_mode)
self.tags_crop.setObjectName("tags_crop")
self.horizontalLayout_10.addWidget(self.tags_crop)
self.tags_stretch = QtWidgets.QRadioButton(parent=self.tags_resize_mode)
self.tags_stretch.setObjectName("tags_stretch")
self.horizontalLayout_10.addWidget(self.tags_stretch)
self.verticalLayout_3.addWidget(self.tags_resize_mode)
self.horizontalLayout_7.addWidget(self.save_to_tags)
self.save_to_file = QtWidgets.QGroupBox(parent=self.resizing)
self.save_to_file.setCheckable(False)
self.save_to_file.setChecked(False)
self.save_to_file.setObjectName("save_to_file")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.save_to_file)
self.verticalLayout_4.setObjectName("verticalLayout_4") self.verticalLayout_4.setObjectName("verticalLayout_4")
self.file_scale_widget = QtWidgets.QWidget(parent=self.save_to_file) self.file_resize_mode = QtWidgets.QComboBox(parent=self.file_scale_down)
self.file_resize_mode.setObjectName("file_resize_mode")
self.verticalLayout_4.addWidget(self.file_resize_mode)
self.file_scale_widget = QtWidgets.QWidget(parent=self.file_scale_down)
self.file_scale_widget.setObjectName("file_scale_widget") self.file_scale_widget.setObjectName("file_scale_widget")
self.horizontalLayout_9 = QtWidgets.QHBoxLayout(self.file_scale_widget) self.horizontalLayout_9 = QtWidgets.QHBoxLayout(self.file_scale_widget)
self.horizontalLayout_9.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_9.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_9.setObjectName("horizontalLayout_9") self.horizontalLayout_9.setObjectName("horizontalLayout_9")
self.file_scale_up = QtWidgets.QCheckBox(parent=self.file_scale_widget)
self.file_scale_up.setObjectName("file_scale_up")
self.horizontalLayout_9.addWidget(self.file_scale_up)
self.file_scale_down = QtWidgets.QCheckBox(parent=self.file_scale_widget)
self.file_scale_down.setObjectName("file_scale_down")
self.horizontalLayout_9.addWidget(self.file_scale_down)
self.verticalLayout_4.addWidget(self.file_scale_widget) self.verticalLayout_4.addWidget(self.file_scale_widget)
self.file_resize_width_widget = QtWidgets.QWidget(parent=self.save_to_file) self.file_resize_width_widget = QtWidgets.QWidget(parent=self.file_scale_down)
self.file_resize_width_widget.setObjectName("file_resize_width_widget") self.file_resize_width_widget.setObjectName("file_resize_width_widget")
self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.file_resize_width_widget) self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.file_resize_width_widget)
self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_6.setSpacing(4) self.horizontalLayout_6.setSpacing(4)
self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.file_resize_width_label = QtWidgets.QCheckBox(parent=self.file_resize_width_widget) self.file_resize_width_label = QtWidgets.QLabel(parent=self.file_resize_width_widget)
self.file_resize_width_label.setObjectName("file_resize_width_label") self.file_resize_width_label.setObjectName("file_resize_width_label")
self.horizontalLayout_6.addWidget(self.file_resize_width_label) self.horizontalLayout_6.addWidget(self.file_resize_width_label)
self.file_resize_width_value = QtWidgets.QSpinBox(parent=self.file_resize_width_widget) self.file_resize_width_value = QtWidgets.QSpinBox(parent=self.file_resize_width_widget)
@@ -212,6 +187,7 @@ class Ui_CoverProcessingOptionsPage(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.file_resize_width_value.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.file_resize_width_value.sizePolicy().hasHeightForWidth())
self.file_resize_width_value.setSizePolicy(sizePolicy) self.file_resize_width_value.setSizePolicy(sizePolicy)
self.file_resize_width_value.setMinimum(1)
self.file_resize_width_value.setMaximum(9999) self.file_resize_width_value.setMaximum(9999)
self.file_resize_width_value.setProperty("value", 1000) self.file_resize_width_value.setProperty("value", 1000)
self.file_resize_width_value.setObjectName("file_resize_width_value") self.file_resize_width_value.setObjectName("file_resize_width_value")
@@ -225,13 +201,13 @@ class Ui_CoverProcessingOptionsPage(object):
self.px_label3.setObjectName("px_label3") self.px_label3.setObjectName("px_label3")
self.horizontalLayout_6.addWidget(self.px_label3) self.horizontalLayout_6.addWidget(self.px_label3)
self.verticalLayout_4.addWidget(self.file_resize_width_widget) self.verticalLayout_4.addWidget(self.file_resize_width_widget)
self.file_resize_height_widget = QtWidgets.QWidget(parent=self.save_to_file) self.file_resize_height_widget = QtWidgets.QWidget(parent=self.file_scale_down)
self.file_resize_height_widget.setObjectName("file_resize_height_widget") self.file_resize_height_widget.setObjectName("file_resize_height_widget")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.file_resize_height_widget) self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.file_resize_height_widget)
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_4.setSpacing(4) self.horizontalLayout_4.setSpacing(4)
self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.file_resize_height_label = QtWidgets.QCheckBox(parent=self.file_resize_height_widget) self.file_resize_height_label = QtWidgets.QLabel(parent=self.file_resize_height_widget)
self.file_resize_height_label.setObjectName("file_resize_height_label") self.file_resize_height_label.setObjectName("file_resize_height_label")
self.horizontalLayout_4.addWidget(self.file_resize_height_label) self.horizontalLayout_4.addWidget(self.file_resize_height_label)
self.file_resize_height_value = QtWidgets.QSpinBox(parent=self.file_resize_height_widget) self.file_resize_height_value = QtWidgets.QSpinBox(parent=self.file_resize_height_widget)
@@ -240,6 +216,7 @@ class Ui_CoverProcessingOptionsPage(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.file_resize_height_value.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.file_resize_height_value.sizePolicy().hasHeightForWidth())
self.file_resize_height_value.setSizePolicy(sizePolicy) self.file_resize_height_value.setSizePolicy(sizePolicy)
self.file_resize_height_value.setMinimum(1)
self.file_resize_height_value.setMaximum(9999) self.file_resize_height_value.setMaximum(9999)
self.file_resize_height_value.setProperty("value", 1000) self.file_resize_height_value.setProperty("value", 1000)
self.file_resize_height_value.setObjectName("file_resize_height_value") self.file_resize_height_value.setObjectName("file_resize_height_value")
@@ -253,24 +230,10 @@ class Ui_CoverProcessingOptionsPage(object):
self.px_label4.setObjectName("px_label4") self.px_label4.setObjectName("px_label4")
self.horizontalLayout_4.addWidget(self.px_label4) self.horizontalLayout_4.addWidget(self.px_label4)
self.verticalLayout_4.addWidget(self.file_resize_height_widget) self.verticalLayout_4.addWidget(self.file_resize_height_widget)
self.file_resize_mode = QtWidgets.QWidget(parent=self.save_to_file) self.file_scale_up = QtWidgets.QCheckBox(parent=self.file_scale_down)
self.file_resize_mode.setObjectName("file_resize_mode") self.file_scale_up.setObjectName("file_scale_up")
self.horizontalLayout_11 = QtWidgets.QHBoxLayout(self.file_resize_mode) self.verticalLayout_4.addWidget(self.file_scale_up)
self.horizontalLayout_11.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_7.addWidget(self.file_scale_down)
self.horizontalLayout_11.setSpacing(2)
self.horizontalLayout_11.setObjectName("horizontalLayout_11")
self.file_keep = QtWidgets.QRadioButton(parent=self.file_resize_mode)
self.file_keep.setChecked(True)
self.file_keep.setObjectName("file_keep")
self.horizontalLayout_11.addWidget(self.file_keep)
self.file_crop = QtWidgets.QRadioButton(parent=self.file_resize_mode)
self.file_crop.setObjectName("file_crop")
self.horizontalLayout_11.addWidget(self.file_crop)
self.file_stretch = QtWidgets.QRadioButton(parent=self.file_resize_mode)
self.file_stretch.setObjectName("file_stretch")
self.horizontalLayout_11.addWidget(self.file_stretch)
self.verticalLayout_4.addWidget(self.file_resize_mode)
self.horizontalLayout_7.addWidget(self.save_to_file)
self.verticalLayout.addWidget(self.resizing) self.verticalLayout.addWidget(self.resizing)
self.converting = QtWidgets.QGroupBox(parent=CoverProcessingOptionsPage) self.converting = QtWidgets.QGroupBox(parent=CoverProcessingOptionsPage)
self.converting.setCheckable(False) self.converting.setCheckable(False)
@@ -288,10 +251,6 @@ class Ui_CoverProcessingOptionsPage(object):
self.horizontalLayout_13.addWidget(self.convert_tags_label) self.horizontalLayout_13.addWidget(self.convert_tags_label)
self.convert_tags_format = QtWidgets.QComboBox(parent=self.convert_tags) self.convert_tags_format = QtWidgets.QComboBox(parent=self.convert_tags)
self.convert_tags_format.setObjectName("convert_tags_format") self.convert_tags_format.setObjectName("convert_tags_format")
self.convert_tags_format.addItem("")
self.convert_tags_format.addItem("")
self.convert_tags_format.addItem("")
self.convert_tags_format.addItem("")
self.horizontalLayout_13.addWidget(self.convert_tags_format) self.horizontalLayout_13.addWidget(self.convert_tags_format)
self.horizontalLayout_12.addWidget(self.convert_tags) self.horizontalLayout_12.addWidget(self.convert_tags)
self.convert_file = QtWidgets.QGroupBox(parent=self.converting) self.convert_file = QtWidgets.QGroupBox(parent=self.converting)
@@ -304,10 +263,6 @@ class Ui_CoverProcessingOptionsPage(object):
self.horizontalLayout_14.addWidget(self.convert_file_label) self.horizontalLayout_14.addWidget(self.convert_file_label)
self.convert_file_format = QtWidgets.QComboBox(parent=self.convert_file) self.convert_file_format = QtWidgets.QComboBox(parent=self.convert_file)
self.convert_file_format.setObjectName("convert_file_format") self.convert_file_format.setObjectName("convert_file_format")
self.convert_file_format.addItem("")
self.convert_file_format.addItem("")
self.convert_file_format.addItem("")
self.convert_file_format.addItem("")
self.horizontalLayout_14.addWidget(self.convert_file_format) self.horizontalLayout_14.addWidget(self.convert_file_format)
self.horizontalLayout_12.addWidget(self.convert_file) self.horizontalLayout_12.addWidget(self.convert_file)
self.verticalLayout.addWidget(self.converting) self.verticalLayout.addWidget(self.converting)
@@ -324,37 +279,21 @@ class Ui_CoverProcessingOptionsPage(object):
self.px_label1.setText(_("px")) self.px_label1.setText(_("px"))
self.filtering_height_label.setText(_("Minimum height:")) self.filtering_height_label.setText(_("Minimum height:"))
self.px_label2.setText(_("px")) self.px_label2.setText(_("px"))
self.resizing.setTitle(_("Resize images if above the given size")) self.resizing.setTitle(_("Resize images to the given size"))
self.save_to_tags.setTitle(_("Resize images saved to tags ")) self.tags_scale_down.setTitle(_("Resize images saved to tags "))
self.tags_scale_up.setText(_("Scale up")) self.tags_resize_width_label.setText(_("Maximum width:"))
self.tags_scale_down.setText(_("Scale down"))
self.tags_resize_width_label.setText(_("Width:"))
self.px_label5.setText(_("px")) self.px_label5.setText(_("px"))
self.tags_resize_height_label.setText(_("Height:")) self.tags_resize_height_label.setText(_("Maximum height:"))
self.px_label6.setText(_("px")) self.px_label6.setText(_("px"))
self.tags_keep.setText(_("Fit")) self.tags_scale_up.setText(_("Enlarge"))
self.tags_crop.setText(_("Fill")) self.file_scale_down.setTitle(_("Resize images saved to files"))
self.tags_stretch.setText(_("Stretch")) self.file_resize_width_label.setText(_("Maximum width:"))
self.save_to_file.setTitle(_("Resize images saved to files"))
self.file_scale_up.setText(_("Scale up"))
self.file_scale_down.setText(_("Scale down"))
self.file_resize_width_label.setText(_("Width:"))
self.px_label3.setText(_("px")) self.px_label3.setText(_("px"))
self.file_resize_height_label.setText(_("Height:")) self.file_resize_height_label.setText(_("Maximum height:"))
self.px_label4.setText(_("px")) self.px_label4.setText(_("px"))
self.file_keep.setText(_("Fit")) self.file_scale_up.setText(_("Enlarge"))
self.file_crop.setText(_("Fill"))
self.file_stretch.setText(_("Stretch"))
self.converting.setTitle(_("Convert images to the given format")) self.converting.setTitle(_("Convert images to the given format"))
self.convert_tags.setTitle(_("Convert images saved to tags")) self.convert_tags.setTitle(_("Convert images saved to tags"))
self.convert_tags_label.setText(_("New format:")) self.convert_tags_label.setText(_("New format:"))
self.convert_tags_format.setItemText(0, _("JPEG"))
self.convert_tags_format.setItemText(1, _("PNG"))
self.convert_tags_format.setItemText(2, _("WebP"))
self.convert_tags_format.setItemText(3, _("TIFF"))
self.convert_file.setTitle(_("Convert images saved to files")) self.convert_file.setTitle(_("Convert images saved to files"))
self.convert_file_label.setText(_("New format:")) self.convert_file_label.setText(_("New format:"))
self.convert_file_format.setItemText(0, _("JPEG"))
self.convert_file_format.setItemText(1, _("PNG"))
self.convert_file_format.setItemText(2, _("WebP"))
self.convert_file_format.setItemText(3, _("TIFF"))

View File

@@ -3,6 +3,7 @@
# Picard, the next-generation MusicBrainz tagger # Picard, the next-generation MusicBrainz tagger
# #
# Copyright (C) 2024 Giorgio Fontanive # Copyright (C) 2024 Giorgio Fontanive
# Copyright (C) 2024 Bob Swift
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@@ -20,7 +21,15 @@
from functools import partial from functools import partial
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QSpinBox
from picard.config import get_config from picard.config import get_config
from picard.const.cover_processing import (
COVER_CONVERTING_FORMATS,
COVER_RESIZE_MODES,
ResizeModes,
)
from picard.extension_points.options_pages import register_options_page from picard.extension_points.options_pages import register_options_page
from picard.i18n import ( from picard.i18n import (
N_, N_,
@@ -47,119 +56,80 @@ class CoverProcessingOptionsPage(OptionsPage):
self.register_setting('filter_cover_by_size') self.register_setting('filter_cover_by_size')
self.register_setting('cover_minimum_width') self.register_setting('cover_minimum_width')
self.register_setting('cover_minimum_height') self.register_setting('cover_minimum_height')
self.register_setting('cover_tags_scale_up') self.register_setting('cover_tags_enlarge')
self.register_setting('cover_tags_scale_down') self.register_setting('cover_tags_resize')
self.register_setting('cover_tags_resize_use_width')
self.register_setting('cover_tags_resize_target_width') self.register_setting('cover_tags_resize_target_width')
self.register_setting('cover_tags_resize_use_height')
self.register_setting('cover_tags_resize_target_height') self.register_setting('cover_tags_resize_target_height')
self.register_setting('cover_tags_stretch') self.register_setting('cover_tags_resize_mode')
self.register_setting('cover_tags_crop')
self.register_setting('cover_tags_convert_images') self.register_setting('cover_tags_convert_images')
self.register_setting('cover_tags_convert_to_format') self.register_setting('cover_tags_convert_to_format')
self.register_setting('cover_file_scale_up') self.register_setting('cover_file_enlarge')
self.register_setting('cover_file_scale_down') self.register_setting('cover_file_resize')
self.register_setting('cover_file_resize_use_width')
self.register_setting('cover_file_resize_target_width') self.register_setting('cover_file_resize_target_width')
self.register_setting('cover_file_resize_use_height')
self.register_setting('cover_file_resize_target_height') self.register_setting('cover_file_resize_target_height')
self.register_setting('cover_file_stretch') self.register_setting('cover_file_resize_mode')
self.register_setting('cover_file_crop')
self.register_setting('cover_file_convert_images') self.register_setting('cover_file_convert_images')
self.register_setting('cover_file_convert_to_format') self.register_setting('cover_file_convert_to_format')
tooltip_keep = N_( for resize_mode in COVER_RESIZE_MODES:
"<p>" self.ui.tags_resize_mode.addItem(resize_mode.title, resize_mode.mode.value)
"Scale the source image so that it fits within the target dimensions." self.ui.file_resize_mode.addItem(resize_mode.title, resize_mode.mode.value)
"</p><p>" self.ui.tags_resize_mode.setItemData(resize_mode.mode, _(resize_mode.tooltip), Qt.ItemDataRole.ToolTipRole)
"One of the final image dimensions may be less than the target dimension if " self.ui.file_resize_mode.setItemData(resize_mode.mode, _(resize_mode.tooltip), Qt.ItemDataRole.ToolTipRole)
"the source image and target dimensions have different aspect ratios."
"</p><p>" self.ui.convert_tags_format.addItems(COVER_CONVERTING_FORMATS)
"For example, a 2000x1000 image resized to target dimensions of " self.ui.convert_file_format.addItems(COVER_CONVERTING_FORMATS)
"1000x1000 would result in a final image size of 1000x500."
"</p>" tags_resize_mode_changed = partial(
self._resize_mode_changed,
self.ui.tags_resize_width_widget,
self.ui.tags_resize_height_widget
) )
self.ui.tags_keep.setToolTip(_(tooltip_keep)) file_resize_mode_changed = partial(
self.ui.file_keep.setToolTip(_(tooltip_keep)) self._resize_mode_changed,
tooltip_crop = ( self.ui.file_resize_width_widget,
"<p>" self.ui.file_resize_height_widget
"Scale the source image so that it completely fills the target dimensions "
"in both directions."
"</p><p>"
"If the source image and target dimensions have different aspect ratios"
"then there will be overflow in one direction which will be (center) cropped."
"</p><p>"
"For example, a 500x1000 image resized to target dimensions of "
"1000x1000 would first scale up to 1000x2000, then the excess height "
"would be center cropped resulting in the final image size of 1000x1000."
"</p>"
) )
self.ui.tags_crop.setToolTip(_(tooltip_crop)) self.ui.tags_resize_mode.currentIndexChanged.connect(tags_resize_mode_changed)
self.ui.file_crop.setToolTip(_(tooltip_crop)) self.ui.file_resize_mode.currentIndexChanged.connect(file_resize_mode_changed)
tooltip_stretch = (
"<p>"
"Stretch the image to exactly fit the specified dimensions, "
"distorting it if necessary."
"</p><p>"
"For example, a 500x1000 image with target dimension of 1000x1000 "
"would be stretched horizontally resulting in the final image "
"size of 1000x1000."
"</p>"
)
self.ui.tags_stretch.setToolTip(_(tooltip_stretch))
self.ui.file_stretch.setToolTip(_(tooltip_stretch))
tags_checkboxes = (self.ui.tags_resize_width_label, self.ui.tags_resize_height_label) def _resize_mode_changed(self, width_widget, height_widget, index):
tags_at_least_one_checked = partial(self._ensure_at_least_one_checked, tags_checkboxes) width_visible = True
for checkbox in tags_checkboxes: height_visible = True
checkbox.clicked.connect(tags_at_least_one_checked) if index == ResizeModes.SCALE_TO_WIDTH:
file_checkboxes = (self.ui.file_resize_width_label, self.ui.file_resize_height_label) height_visible = False
file_at_least_one_checked = partial(self._ensure_at_least_one_checked, file_checkboxes) elif index == ResizeModes.SCALE_TO_HEIGHT:
for checkbox in file_checkboxes: width_visible = False
checkbox.clicked.connect(file_at_least_one_checked) width_widget.setEnabled(width_visible)
width_spinbox = width_widget.findChildren(QSpinBox)[0]
self._spinboxes = { width_spinbox.lineEdit().setVisible(width_visible)
self.ui.tags_resize_width_label: self.ui.tags_resize_width_value, height_widget.setEnabled(height_visible)
self.ui.tags_resize_height_label: self.ui.tags_resize_height_value, height_spinbox = height_widget.findChildren(QSpinBox)[0]
self.ui.file_resize_width_label: self.ui.file_resize_width_value, height_spinbox.lineEdit().setVisible(height_visible)
self.ui.file_resize_height_label: self.ui.file_resize_height_value,
}
for checkbox, spinbox in self._spinboxes.items():
spinbox.setEnabled(checkbox.isChecked())
checkbox.stateChanged.connect(self._update_resize_spinboxes)
def _update_resize_spinboxes(self):
spinbox = self._spinboxes[self.sender()]
spinbox.setEnabled(self.sender().isChecked())
def _ensure_at_least_one_checked(self, checkboxes, clicked):
if not clicked and not any(checkbox.isChecked() for checkbox in checkboxes):
sender = self.sender()
sender.setChecked(True)
def load(self): def load(self):
config = get_config() config = get_config()
self.ui.filtering.setChecked(config.setting['filter_cover_by_size']) self.ui.filtering.setChecked(config.setting['filter_cover_by_size'])
self.ui.filtering_width_value.setValue(config.setting['cover_minimum_width']) self.ui.filtering_width_value.setValue(config.setting['cover_minimum_width'])
self.ui.filtering_height_value.setValue(config.setting['cover_minimum_height']) self.ui.filtering_height_value.setValue(config.setting['cover_minimum_height'])
self.ui.tags_scale_up.setChecked(config.setting['cover_tags_scale_up']) self.ui.tags_scale_up.setChecked(config.setting['cover_tags_enlarge'])
self.ui.tags_scale_down.setChecked(config.setting['cover_tags_scale_down']) self.ui.tags_scale_down.setChecked(config.setting['cover_tags_resize'])
self.ui.tags_resize_width_label.setChecked(config.setting['cover_tags_resize_use_width'])
self.ui.tags_resize_width_value.setValue(config.setting['cover_tags_resize_target_width']) self.ui.tags_resize_width_value.setValue(config.setting['cover_tags_resize_target_width'])
self.ui.tags_resize_height_label.setChecked(config.setting['cover_tags_resize_use_height'])
self.ui.tags_resize_height_value.setValue(config.setting['cover_tags_resize_target_height']) self.ui.tags_resize_height_value.setValue(config.setting['cover_tags_resize_target_height'])
self.ui.tags_stretch.setChecked(config.setting['cover_tags_stretch']) current_index = self.ui.tags_resize_mode.findData(config.setting['cover_tags_resize_mode'])
self.ui.tags_crop.setChecked(config.setting['cover_tags_crop']) if current_index == -1:
current_index = ResizeModes.MAINTAIN_ASPECT_RATIO
self.ui.tags_resize_mode.setCurrentIndex(current_index)
self.ui.convert_tags.setChecked(config.setting['cover_tags_convert_images']) self.ui.convert_tags.setChecked(config.setting['cover_tags_convert_images'])
self.ui.convert_tags_format.setCurrentText(config.setting['cover_tags_convert_to_format']) self.ui.convert_tags_format.setCurrentText(config.setting['cover_tags_convert_to_format'])
self.ui.file_scale_up.setChecked(config.setting['cover_file_scale_up']) self.ui.file_scale_up.setChecked(config.setting['cover_file_enlarge'])
self.ui.file_scale_down.setChecked(config.setting['cover_file_scale_down']) self.ui.file_scale_down.setChecked(config.setting['cover_file_resize'])
self.ui.file_resize_width_label.setChecked(config.setting['cover_file_resize_use_width'])
self.ui.file_resize_width_value.setValue(config.setting['cover_file_resize_target_width']) self.ui.file_resize_width_value.setValue(config.setting['cover_file_resize_target_width'])
self.ui.file_resize_height_label.setChecked(config.setting['cover_file_resize_use_height'])
self.ui.file_resize_height_value.setValue(config.setting['cover_file_resize_target_height']) self.ui.file_resize_height_value.setValue(config.setting['cover_file_resize_target_height'])
self.ui.file_stretch.setChecked(config.setting['cover_file_stretch']) current_index = self.ui.file_resize_mode.findData(config.setting['cover_file_resize_mode'])
self.ui.file_crop.setChecked(config.setting['cover_file_crop']) if current_index == -1:
current_index = ResizeModes.MAINTAIN_ASPECT_RATIO
self.ui.file_resize_mode.setCurrentIndex(current_index)
self.ui.convert_file.setChecked(config.setting['cover_file_convert_images']) self.ui.convert_file.setChecked(config.setting['cover_file_convert_images'])
self.ui.convert_file_format.setCurrentText(config.setting['cover_file_convert_to_format']) self.ui.convert_file_format.setCurrentText(config.setting['cover_file_convert_to_format'])
@@ -168,24 +138,18 @@ class CoverProcessingOptionsPage(OptionsPage):
config.setting['filter_cover_by_size'] = self.ui.filtering.isChecked() config.setting['filter_cover_by_size'] = self.ui.filtering.isChecked()
config.setting['cover_minimum_width'] = self.ui.filtering_width_value.value() config.setting['cover_minimum_width'] = self.ui.filtering_width_value.value()
config.setting['cover_minimum_height'] = self.ui.filtering_height_value.value() config.setting['cover_minimum_height'] = self.ui.filtering_height_value.value()
config.setting['cover_tags_scale_up'] = self.ui.tags_scale_up.isChecked() config.setting['cover_tags_enlarge'] = self.ui.tags_scale_up.isChecked()
config.setting['cover_tags_scale_down'] = self.ui.tags_scale_down.isChecked() config.setting['cover_tags_resize'] = self.ui.tags_scale_down.isChecked()
config.setting['cover_tags_resize_use_width'] = self.ui.tags_resize_width_label.isChecked()
config.setting['cover_tags_resize_target_width'] = self.ui.tags_resize_width_value.value() config.setting['cover_tags_resize_target_width'] = self.ui.tags_resize_width_value.value()
config.setting['cover_tags_resize_use_height'] = self.ui.tags_resize_height_label.isChecked()
config.setting['cover_tags_resize_target_height'] = self.ui.tags_resize_height_value.value() config.setting['cover_tags_resize_target_height'] = self.ui.tags_resize_height_value.value()
config.setting['cover_tags_stretch'] = self.ui.tags_stretch.isChecked() config.setting['cover_tags_resize_mode'] = self.ui.tags_resize_mode.currentData()
config.setting['cover_tags_crop'] = self.ui.tags_crop.isChecked()
config.setting['cover_tags_convert_images'] = self.ui.convert_tags.isChecked() config.setting['cover_tags_convert_images'] = self.ui.convert_tags.isChecked()
config.setting['cover_tags_convert_to_format'] = self.ui.convert_tags_format.currentText() config.setting['cover_tags_convert_to_format'] = self.ui.convert_tags_format.currentText()
config.setting['cover_file_scale_up'] = self.ui.file_scale_up.isChecked() config.setting['cover_file_enlarge'] = self.ui.file_scale_up.isChecked()
config.setting['cover_file_scale_down'] = self.ui.file_scale_down.isChecked() config.setting['cover_file_resize'] = self.ui.file_scale_down.isChecked()
config.setting['cover_file_resize_use_width'] = self.ui.file_resize_width_label.isChecked()
config.setting['cover_file_resize_target_width'] = self.ui.file_resize_width_value.value() config.setting['cover_file_resize_target_width'] = self.ui.file_resize_width_value.value()
config.setting['cover_file_resize_use_height'] = self.ui.file_resize_height_label.isChecked()
config.setting['cover_file_resize_target_height'] = self.ui.file_resize_height_value.value() config.setting['cover_file_resize_target_height'] = self.ui.file_resize_height_value.value()
config.setting['cover_file_stretch'] = self.ui.file_stretch.isChecked() config.setting['cover_file_resize_mode'] = self.ui.file_resize_mode.currentData()
config.setting['cover_file_crop'] = self.ui.file_crop.isChecked()
config.setting['cover_file_convert_images'] = self.ui.convert_file.isChecked() config.setting['cover_file_convert_images'] = self.ui.convert_file.isChecked()
config.setting['cover_file_convert_to_format'] = self.ui.convert_file_format.currentText() config.setting['cover_file_convert_to_format'] = self.ui.convert_file_format.currentText()

View File

@@ -26,6 +26,7 @@ from PyQt6.QtGui import QImage
from test.picardtestcase import PicardTestCase from test.picardtestcase import PicardTestCase
from picard import config from picard import config
from picard.const.cover_processing import ResizeModes
from picard.coverart.image import CoverArtImage from picard.coverart.image import CoverArtImage
from picard.coverart.processing import run_image_processors from picard.coverart.processing import run_image_processors
from picard.coverart.processing.filters import ( from picard.coverart.processing.filters import (
@@ -84,24 +85,18 @@ class ImageProcessorsTest(PicardTestCase):
super().setUp() super().setUp()
self.settings = { self.settings = {
'enabled_plugins': [], 'enabled_plugins': [],
'cover_tags_scale_up': True, 'cover_tags_resize': True,
'cover_tags_scale_down': True, 'cover_tags_enlarge': True,
'cover_tags_resize_use_width': True,
'cover_tags_resize_target_width': 500, 'cover_tags_resize_target_width': 500,
'cover_tags_resize_use_height': True,
'cover_tags_resize_target_height': 500, 'cover_tags_resize_target_height': 500,
'cover_tags_stretch': False, 'cover_tags_resize_mode': ResizeModes.MAINTAIN_ASPECT_RATIO,
'cover_tags_crop': False,
'cover_tags_convert_images': False, 'cover_tags_convert_images': False,
'cover_tags_convert_to_format': 'jpeg', 'cover_tags_convert_to_format': 'jpeg',
'cover_file_scale_up': True, 'cover_file_resize': True,
'cover_file_scale_down': True, 'cover_file_enlarge': True,
'cover_file_resize_use_width': True,
'cover_file_resize_target_width': 750, 'cover_file_resize_target_width': 750,
'cover_file_resize_use_height': True,
'cover_file_resize_target_height': 750, 'cover_file_resize_target_height': 750,
'cover_file_stretch': False, 'cover_file_resize_mode': ResizeModes.MAINTAIN_ASPECT_RATIO,
'cover_file_crop': False,
'save_images_to_tags': True, 'save_images_to_tags': True,
'save_images_to_files': True, 'save_images_to_files': True,
'cover_file_convert_images': False, 'cover_file_convert_images': False,
@@ -162,7 +157,7 @@ class ImageProcessorsTest(PicardTestCase):
image = ProcessingImage(create_fake_image(size[0], size[1], 'jpg')) image = ProcessingImage(create_fake_image(size[0], size[1], 'jpg'))
processor = ResizeImage() processor = ResizeImage()
processor.run(image, ProcessingTarget.TAGS) processor.run(image, ProcessingTarget.TAGS)
new_size = (image.get_result().width(), image.get_result().height()) new_size = (image.get_qimage().width(), image.get_qimage().height())
new_info_size = (image.info.width, image.info.height) new_info_size = (image.info.width, image.info.height)
self.assertEqual(new_size, expected_size) self.assertEqual(new_size, expected_size)
self.assertEqual(new_info_size, expected_size) self.assertEqual(new_info_size, expected_size)
@@ -175,7 +170,7 @@ class ImageProcessorsTest(PicardTestCase):
def test_scale_down_only_width(self): def test_scale_down_only_width(self):
settings = copy(self.settings) settings = copy(self.settings)
settings['cover_tags_resize_use_height'] = False settings['cover_tags_resize_mode'] = ResizeModes.SCALE_TO_WIDTH
self.set_config_values(settings) self.set_config_values(settings)
self._check_resize_image((1000, 1000), (500, 500)) self._check_resize_image((1000, 1000), (500, 500))
self._check_resize_image((1000, 500), (500, 250)) self._check_resize_image((1000, 500), (500, 250))
@@ -184,7 +179,7 @@ class ImageProcessorsTest(PicardTestCase):
def test_scale_down_only_height(self): def test_scale_down_only_height(self):
settings = copy(self.settings) settings = copy(self.settings)
settings['cover_tags_resize_use_width'] = False settings['cover_tags_resize_mode'] = ResizeModes.SCALE_TO_HEIGHT
self.set_config_values(settings) self.set_config_values(settings)
self._check_resize_image((1000, 1000), (500, 500)) self._check_resize_image((1000, 1000), (500, 500))
self._check_resize_image((1000, 500), (1000, 500)) self._check_resize_image((1000, 500), (1000, 500))
@@ -199,7 +194,7 @@ class ImageProcessorsTest(PicardTestCase):
def test_scale_up_only_width(self): def test_scale_up_only_width(self):
settings = copy(self.settings) settings = copy(self.settings)
settings['cover_tags_resize_use_height'] = False settings['cover_tags_resize_mode'] = ResizeModes.SCALE_TO_WIDTH
self.set_config_values(settings) self.set_config_values(settings)
self._check_resize_image((250, 250), (500, 500)) self._check_resize_image((250, 250), (500, 500))
self._check_resize_image((400, 500), (500, 625)) self._check_resize_image((400, 500), (500, 625))
@@ -208,7 +203,7 @@ class ImageProcessorsTest(PicardTestCase):
def test_scale_up_only_height(self): def test_scale_up_only_height(self):
settings = copy(self.settings) settings = copy(self.settings)
settings['cover_tags_resize_use_width'] = False settings['cover_tags_resize_mode'] = ResizeModes.SCALE_TO_HEIGHT
self.set_config_values(settings) self.set_config_values(settings)
self._check_resize_image((250, 250), (500, 500)) self._check_resize_image((250, 250), (500, 500))
self._check_resize_image((400, 500), (400, 500)) self._check_resize_image((400, 500), (400, 500))
@@ -225,67 +220,27 @@ class ImageProcessorsTest(PicardTestCase):
def test_stretch_both_dimensions(self): def test_stretch_both_dimensions(self):
settings = copy(self.settings) settings = copy(self.settings)
settings['cover_tags_stretch'] = True settings['cover_tags_resize_mode'] = ResizeModes.STRETCH_TO_FIT
self.set_config_values(settings) self.set_config_values(settings)
self._check_resize_image((1000, 100), (500, 500)) self._check_resize_image((1000, 100), (500, 500))
self._check_resize_image((200, 500), (500, 500)) self._check_resize_image((200, 500), (500, 500))
self._check_resize_image((200, 2000), (500, 500)) self._check_resize_image((200, 2000), (500, 500))
self.set_config_values(self.settings) self.set_config_values(self.settings)
def test_stretch_only_width(self):
settings = copy(self.settings)
settings['cover_tags_stretch'] = True
settings['cover_tags_resize_use_height'] = False
self.set_config_values(settings)
self._check_resize_image((1000, 100), (500, 100))
self._check_resize_image((200, 500), (500, 500))
self._check_resize_image((200, 2000), (500, 2000))
self.set_config_values(self.settings)
def test_stretch_only_height(self):
settings = copy(self.settings)
settings['cover_tags_stretch'] = True
settings['cover_tags_resize_use_width'] = False
self.set_config_values(settings)
self._check_resize_image((1000, 100), (1000, 500))
self._check_resize_image((200, 500), (200, 500))
self._check_resize_image((200, 2000), (200, 500))
self.set_config_values(self.settings)
def test_crop_both_dimensions(self): def test_crop_both_dimensions(self):
settings = copy(self.settings) settings = copy(self.settings)
settings['cover_tags_crop'] = True settings['cover_tags_resize_mode'] = ResizeModes.CROP_TO_FIT
self.set_config_values(settings) self.set_config_values(settings)
self._check_resize_image((1000, 100), (500, 500)) self._check_resize_image((1000, 100), (500, 500))
self._check_resize_image((750, 1000), (500, 500)) self._check_resize_image((750, 1000), (500, 500))
self._check_resize_image((250, 1000), (500, 500)) self._check_resize_image((250, 1000), (500, 500))
self.set_config_values(self.settings) self.set_config_values(self.settings)
def test_crop_only_width(self):
settings = copy(self.settings)
settings['cover_tags_crop'] = True
settings['cover_tags_resize_use_height'] = False
self.set_config_values(settings)
self._check_resize_image((1000, 100), (500, 100))
self._check_resize_image((750, 1000), (500, 1000))
self._check_resize_image((250, 1000), (500, 1000))
self.set_config_values(self.settings)
def test_crop_only_height(self):
settings = copy(self.settings)
settings['cover_tags_crop'] = True
settings['cover_tags_resize_use_width'] = False
self.set_config_values(settings)
self._check_resize_image((1000, 100), (1000, 500))
self._check_resize_image((750, 1000), (750, 500))
self._check_resize_image((250, 1000), (250, 500))
self.set_config_values(self.settings)
def _check_convert_image(self, format, expected_format): def _check_convert_image(self, format, expected_format):
image = ProcessingImage(create_fake_image(100, 100, format)) image = ProcessingImage(create_fake_image(100, 100, format))
processor = ConvertImage() processor = ConvertImage()
processor.run(image, ProcessingTarget.TAGS) processor.run(image, ProcessingTarget.TAGS)
new_image = image.get_result(default_format=True) new_image = image.get_result()
new_info = imageinfo.identify(new_image) new_info = imageinfo.identify(new_image)
self.assertIn(new_info.format, ConvertImage._format_aliases[expected_format]) self.assertIn(new_info.format, ConvertImage._format_aliases[expected_format])

View File

@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>529</width> <width>535</width>
<height>467</height> <height>475</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -140,7 +140,7 @@
<item> <item>
<widget class="QGroupBox" name="resizing"> <widget class="QGroupBox" name="resizing">
<property name="title"> <property name="title">
<string>Resize images if above the given size</string> <string>Resize images to the given size</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>false</bool> <bool>false</bool>
@@ -150,48 +150,19 @@
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item> <item>
<widget class="QGroupBox" name="save_to_tags"> <widget class="QGroupBox" name="tags_scale_down">
<property name="title"> <property name="title">
<string>Resize images saved to tags </string> <string>Resize images saved to tags </string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>false</bool> <bool>true</bool>
</property> </property>
<property name="checked"> <property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<widget class="QWidget" name="tags_scale_widget" native="true"> <widget class="QComboBox" name="tags_resize_mode"/>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="tags_scale_up">
<property name="text">
<string>Scale up</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="tags_scale_down">
<property name="text">
<string>Scale down</string>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="QWidget" name="tags_resize_width_widget" native="true"> <widget class="QWidget" name="tags_resize_width_widget" native="true">
@@ -212,9 +183,9 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QCheckBox" name="tags_resize_width_label"> <widget class="QLabel" name="tags_resize_width_label">
<property name="text"> <property name="text">
<string>Width:</string> <string>Maximum width:</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -226,6 +197,9 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum"> <property name="maximum">
<number>9999</number> <number>9999</number>
</property> </property>
@@ -269,9 +243,9 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QCheckBox" name="tags_resize_height_label"> <widget class="QLabel" name="tags_resize_height_label">
<property name="text"> <property name="text">
<string>Height:</string> <string>Maximum height:</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -283,6 +257,9 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum"> <property name="maximum">
<number>9999</number> <number>9999</number>
</property> </property>
@@ -308,65 +285,27 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QWidget" name="tags_resize_mode" native="true"> <widget class="QCheckBox" name="tags_scale_up">
<layout class="QHBoxLayout" name="horizontalLayout_10"> <property name="text">
<property name="spacing"> <string>Enlarge</string>
<number>2</number> </property>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="tags_keep">
<property name="text">
<string>Fit</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="tags_crop">
<property name="text">
<string>Fill</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="tags_stretch">
<property name="text">
<string>Stretch</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="save_to_file"> <widget class="QGroupBox" name="file_scale_down">
<property name="title"> <property name="title">
<string>Resize images saved to files</string> <string>Resize images saved to files</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>false</bool> <bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QComboBox" name="file_resize_mode"/>
</item>
<item> <item>
<widget class="QWidget" name="file_scale_widget" native="true"> <widget class="QWidget" name="file_scale_widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_9"> <layout class="QHBoxLayout" name="horizontalLayout_9">
@@ -382,20 +321,6 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<widget class="QCheckBox" name="file_scale_up">
<property name="text">
<string>Scale up</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="file_scale_down">
<property name="text">
<string>Scale down</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -418,9 +343,9 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QCheckBox" name="file_resize_width_label"> <widget class="QLabel" name="file_resize_width_label">
<property name="text"> <property name="text">
<string>Width:</string> <string>Maximum width:</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -432,6 +357,9 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum"> <property name="maximum">
<number>9999</number> <number>9999</number>
</property> </property>
@@ -475,9 +403,9 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QCheckBox" name="file_resize_height_label"> <widget class="QLabel" name="file_resize_height_label">
<property name="text"> <property name="text">
<string>Height:</string> <string>Maximum height:</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -489,6 +417,9 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum"> <property name="maximum">
<number>9999</number> <number>9999</number>
</property> </property>
@@ -514,48 +445,10 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QWidget" name="file_resize_mode" native="true"> <widget class="QCheckBox" name="file_scale_up">
<layout class="QHBoxLayout" name="horizontalLayout_11"> <property name="text">
<property name="spacing"> <string>Enlarge</string>
<number>2</number> </property>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="file_keep">
<property name="text">
<string>Fit</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="file_crop">
<property name="text">
<string>Fill</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="file_stretch">
<property name="text">
<string>Stretch</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
@@ -593,28 +486,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="convert_tags_format"> <widget class="QComboBox" name="convert_tags_format"/>
<item>
<property name="text">
<string>JPEG</string>
</property>
</item>
<item>
<property name="text">
<string>PNG</string>
</property>
</item>
<item>
<property name="text">
<string>WebP</string>
</property>
</item>
<item>
<property name="text">
<string>TIFF</string>
</property>
</item>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
@@ -636,28 +508,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="convert_file_format"> <widget class="QComboBox" name="convert_file_format"/>
<item>
<property name="text">
<string>JPEG</string>
</property>
</item>
<item>
<property name="text">
<string>PNG</string>
</property>
</item>
<item>
<property name="text">
<string>WebP</string>
</property>
</item>
<item>
<property name="text">
<string>TIFF</string>
</property>
</item>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>