Add support for saving multiple metadata styles in the GUI

Unwind credit color comprehension

Convert save style from a string setting to a list

Use lordwelch version of Checkable combobox

Improve readbility, fix label alignment in taggerwindow.ui, better report removal of tags and clearer number meanings.

Unwind list comprehension for easier readability
This commit is contained in:
Mizaki 2024-01-27 01:41:48 +00:00
parent bc335f1686
commit 23021ba632
7 changed files with 297 additions and 125 deletions

View File

@ -39,7 +39,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self,
parent: QtWidgets.QWidget,
match_set_list: list[Result],
style: str,
styles: list[str],
fetch_func: Callable[[IssueResult], GenericMetadata],
config: ct_ns,
talker: ComicTalker,
@ -81,7 +81,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
self.match_set_list = match_set_list
self._style = style
self._styles = styles
self.fetch_func = fetch_func
self.current_match_set_idx = 0
@ -230,7 +230,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
match = self.current_match()
ca = ComicArchive(self.current_match_set.original_path)
md = ca.read_metadata(self._style)
md = ca.read_metadata(self.config.internal__load_data_style)
if md.is_empty:
md = ca.metadata_from_filename(
self.config.Filename_Parsing__complicated_parser,
@ -247,10 +247,10 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
md.overlay(ct_md)
success = ca.write_metadata(md, self._style)
for style in self._styles:
success = ca.write_metadata(md, style)
QtWidgets.QApplication.restoreOverrideCursor()
if not success:
QtWidgets.QMessageBox.warning(self, "Write Error", "Saving the tags to the archive seemed to fail!")
ca.load_cache(list(metadata_styles))
QtWidgets.QApplication.restoreOverrideCursor()
if not success:
QtWidgets.QMessageBox.warning(self, "Write Error", "Saving the tags to the archive seemed to fail!")

View File

@ -23,7 +23,7 @@ def general(parser: settngs.Manager) -> None:
def internal(parser: settngs.Manager) -> None:
# automatic settings
parser.add_setting("install_id", default=uuid.uuid4().hex, cmdline=False)
parser.add_setting("save_data_style", default="cbi", cmdline=False)
parser.add_setting("save_data_style", default=["cbi"], cmdline=False)
parser.add_setting("load_data_style", default="cbi", cmdline=False)
parser.add_setting("last_opened_folder", default="", cmdline=False)
parser.add_setting("window_width", default=0, cmdline=False)
@ -255,6 +255,11 @@ def parse_filter(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
def validate_file_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
config = parse_filter(config)
# TODO Remove this conversion check at a later date
if isinstance(config[0].internal__save_data_style, str):
config[0].internal__save_data_style = [config[0].internal__save_data_style]
if config[0].Filename_Parsing__protofolius_issue_number_scheme:
config[0].Filename_Parsing__allow_issue_start_with_letter = True

View File

@ -37,7 +37,7 @@ class settngs_namespace(settngs.TypedNS):
Runtime_Options__files: list[str]
internal__install_id: str
internal__save_data_style: str
internal__save_data_style: list[str]
internal__load_data_style: str
internal__last_opened_folder: str
internal__window_width: int

View File

@ -115,7 +115,7 @@ class PageListEditor(QtWidgets.QWidget):
self.comic_archive: ComicArchive | None = None
self.pages_list: list[ImageMetadata] = []
self.data_style = ""
self.data_styles: list[str] = []
def reset_page(self) -> None:
self.pageWidget.clear()
@ -326,7 +326,7 @@ class PageListEditor(QtWidgets.QWidget):
self.comic_archive = comic_archive
self.pages_list = pages_list
if pages_list:
self.set_metadata_style(self.data_style)
self.set_metadata_style(self.data_styles)
else:
self.cbPageType.setEnabled(False)
self.chkDoublePage.setEnabled(False)
@ -370,11 +370,16 @@ class PageListEditor(QtWidgets.QWidget):
self.first_front_page = self.get_first_front_cover()
self.firstFrontCoverChanged.emit(self.first_front_page)
def set_metadata_style(self, data_style: str) -> None:
def set_metadata_style(self, data_styles: list[str]) -> None:
# depending on the current data style, certain fields are disabled
if data_style:
self.metadata_style = data_style
if data_styles:
styles = [metadata_styles[style] for style in data_styles]
enabled_widgets = []
for style in styles:
for attr in style.supported_attributes:
enabled_widgets.append(attr)
self.data_styles = data_styles
enabled_widgets = metadata_styles[data_style].supported_attributes
for metadata, widget in self.md_attributes.items():
enable_widget(widget, metadata in enabled_widgets)

View File

@ -61,6 +61,7 @@ from comictaggerlib.resulttypes import Action, IssueResult, MatchStatus, OnlineM
from comictaggerlib.seriesselectionwindow import SeriesSelectionWindow
from comictaggerlib.settingswindow import SettingsWindow
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.customwidgets import CheckableComboBox
from comictaggerlib.ui.qtutils import center_window_on_parent, enable_widget, reduce_widget_font_size
from comictaggerlib.versionchecker import VersionChecker
from comictalker.comictalker import ComicTalker, TalkerError
@ -215,15 +216,29 @@ class TaggerWindow(QtWidgets.QMainWindow):
if config[0].Runtime_Options__type and isinstance(config[0].Runtime_Options__type[0], str):
# respect the command line option tag type
config[0].internal__save_data_style = config[0].Runtime_Options__type[0]
config[0].internal__save_data_style = config[0].Runtime_Options__type
config[0].internal__load_data_style = config[0].Runtime_Options__type[0]
if config[0].internal__save_data_style not in metadata_styles:
config[0].internal__save_data_style = list(metadata_styles.keys())[0]
for style in config[0].internal__save_data_style:
if style not in metadata_styles:
config[0].internal__save_data_style.remove(style)
if config[0].internal__load_data_style not in metadata_styles:
config[0].internal__load_data_style = list(metadata_styles.keys())[0]
self.save_data_style = config[0].internal__save_data_style
self.load_data_style = config[0].internal__load_data_style
self.save_data_styles: list[str] = config[0].internal__save_data_style
self.load_data_style: str = config[0].internal__load_data_style
# Add multiselect combobox
self.cbSaveDataStyle = CheckableComboBox()
self.cbSaveDataStyle.setToolTip("At least 1 save style is required")
# Add normal combobox for read style (TODO support multiple read styles)
self.cbLoadDataStyle = QtWidgets.QComboBox()
# Need to set minimum or source_style_formLayout will resize larger than 230px which will affect the
# file info box and cover image width underneath
self.cbLoadDataStyle.setMinimumWidth(100)
self.cbSaveDataStyle.setMinimumWidth(100)
self.source_style_formLayout.addRow("Read Style", self.cbLoadDataStyle)
self.source_style_formLayout.addRow("Modify Styles", self.cbSaveDataStyle)
self.setAcceptDrops(True)
self.view_tag_actions, self.remove_tag_actions = self.tag_actions()
@ -273,7 +288,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
# hook up the callbacks
self.cbLoadDataStyle.currentIndexChanged.connect(self.set_load_data_style)
self.cbSaveDataStyle.currentIndexChanged.connect(self.set_save_data_style)
self.cbSaveDataStyle.itemChecked.connect(self.set_save_data_style)
self.cbx_sources.currentIndexChanged.connect(self.set_source)
self.btnEditCredit.clicked.connect(self.edit_credit)
self.btnAddCredit.clicked.connect(self.add_credit)
@ -438,7 +453,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.actionCopyTags.triggered.connect(self.copy_tags)
self.actionRemoveAuto.setShortcut("Ctrl+D")
self.actionRemoveAuto.setStatusTip("Remove currently selected modify tag style from the archive")
self.actionRemoveAuto.setStatusTip("Remove currently selected modify tag style(s) from the archive")
self.actionRemoveAuto.triggered.connect(self.remove_auto)
self.actionRepackage.setShortcut("Ctrl+E")
@ -1146,7 +1161,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
reply = QtWidgets.QMessageBox.question(
self,
"Save Tags",
f"Are you sure you wish to save {metadata_styles[self.save_data_style].name()} tags to this archive?",
f"Are you sure you wish to save {', '.join([metadata_styles[style].name() for style in self.save_data_styles])} tags to this archive?",
QtWidgets.QMessageBox.StandardButton.Yes,
QtWidgets.QMessageBox.StandardButton.No,
)
@ -1155,12 +1170,22 @@ class TaggerWindow(QtWidgets.QMainWindow):
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
self.form_to_metadata()
success = self.comic_archive.write_metadata(self.metadata, self.save_data_style)
fail_list = []
# Save each style
for style in self.save_data_styles:
success = self.comic_archive.write_metadata(self.metadata, style)
if not success:
fail_list.append(style)
self.comic_archive.load_cache(list(metadata_styles))
QtWidgets.QApplication.restoreOverrideCursor()
if not success:
QtWidgets.QMessageBox.warning(self, "Save failed", "The tag save operation seemed to fail!")
if fail_list:
QtWidgets.QMessageBox.warning(
self,
"Save failed",
f"The tag save operation seemed to fail for:{', '.join([metadata_styles[style].name() for style in fail_list])}",
)
else:
self.clear_dirty_flag()
self.update_info_box()
@ -1186,9 +1211,9 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.adjust_load_style_combo()
self.cbLoadDataStyle.currentIndexChanged.connect(self.set_load_data_style)
def set_save_data_style(self, s: str) -> None:
self.save_data_style = self.cbSaveDataStyle.itemData(s)
self.config[0].internal__save_data_style = self.save_data_style
def set_save_data_style(self) -> None:
self.save_data_styles = self.cbSaveDataStyle.currentData()
self.config[0].internal__save_data_style = self.save_data_styles
self.update_metadata_style_tweaks()
self.update_menus()
@ -1196,13 +1221,18 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.config[0].Sources__source = self.cbx_sources.itemData(s)
def update_metadata_credit_colors(self) -> None:
style = metadata_styles[self.save_data_style]
enabled = style.supported_attributes
styles = [metadata_styles[style] for style in self.save_data_styles]
enabled = []
for style in styles:
for attr in style.supported_attributes:
enabled.append(attr)
credit_attributes = [x for x in self.md_attributes.items() if "credits." in x[0]]
for r in range(self.twCredits.rowCount()):
w = self.twCredits.item(r, 1)
supports_role = style.supports_credit_role(str(w.text()))
supports_role = any(style.supports_credit_role(str(w.text())) for style in styles)
for credit in credit_attributes:
widget_enabled = credit[0] in enabled
widget = self.twCredits.item(r, credit[1])
@ -1212,14 +1242,17 @@ class TaggerWindow(QtWidgets.QMainWindow):
def update_metadata_style_tweaks(self) -> None:
# depending on the current data style, certain fields are disabled
enabled_widgets = []
for style in self.save_data_styles:
for attr in metadata_styles[style].supported_attributes:
enabled_widgets.append(attr)
enabled_widgets = metadata_styles[self.save_data_style].supported_attributes
for metadata, widget in self.md_attributes.items():
if widget is not None and not isinstance(widget, (int)):
enable_widget(widget, metadata in enabled_widgets)
self.update_metadata_credit_colors()
self.page_list_editor.set_metadata_style(self.save_data_style)
self.page_list_editor.set_metadata_style(self.save_data_styles)
def cell_double_clicked(self, r: int, c: int) -> None:
self.edit_credit()
@ -1353,7 +1386,11 @@ class TaggerWindow(QtWidgets.QMainWindow):
def adjust_save_style_combo(self) -> None:
# select the current style
self.cbSaveDataStyle.setCurrentIndex(self.cbSaveDataStyle.findData(self.save_data_style))
unchecked = set(metadata_styles.keys()) - set(self.save_data_styles)
for style in self.save_data_styles:
self.cbSaveDataStyle.setItemChecked(self.cbLoadDataStyle.findData(style), True)
for style in unchecked:
self.cbSaveDataStyle.setItemChecked(self.cbLoadDataStyle.findData(style), False)
self.update_metadata_style_tweaks()
def populate_combo_boxes(self) -> None:
@ -1461,19 +1498,26 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.cbFormat.addItem("Year One")
def remove_auto(self) -> None:
self.remove_tags(self.save_data_style)
self.remove_tags(self.save_data_styles)
def remove_tags(self, style: str) -> None:
def remove_tags(self, styles: list[str]) -> None:
# remove the indicated tags from the archive
ca_list = self.fileSelectionList.get_selected_archive_list()
has_md_count = 0
file_md_count = {}
for style in styles:
file_md_count[style] = 0
for ca in ca_list:
if ca.has_metadata(style):
has_md_count += 1
for style in styles:
if ca.has_metadata(style):
has_md_count += 1
file_md_count[style] += 1
if has_md_count == 0:
QtWidgets.QMessageBox.information(
self, "Remove Tags", f"No archives with {metadata_styles[style].name()} tags selected!"
self,
"Remove Tags",
f"No archives with {', '.join([metadata_styles[style].name() for style in styles])} tags selected!",
)
return
@ -1486,7 +1530,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
reply = QtWidgets.QMessageBox.question(
self,
"Remove Tags",
f"Are you sure you wish to remove the {metadata_styles[style].name()} tags from {has_md_count} archive(s)?",
f"Are you sure you wish to remove {', '.join([f'{metadata_styles[style].name()} tags from {count} files' for style, count in file_md_count.items()])} removing a total of {has_md_count} tag(s)?",
QtWidgets.QMessageBox.StandardButton.Yes,
QtWidgets.QMessageBox.StandardButton.No,
)
@ -1508,12 +1552,15 @@ class TaggerWindow(QtWidgets.QMainWindow):
progdialog.setValue(prog_idx)
progdialog.setLabelText(str(ca.path))
QtCore.QCoreApplication.processEvents()
if ca.has_metadata(style) and ca.is_writable():
if not ca.remove_metadata(style):
failed_list.append(ca.path)
else:
success_count += 1
ca.load_cache(list(metadata_styles))
for style in styles:
if ca.has_metadata(style) and ca.is_writable():
if not ca.remove_metadata(style):
failed_list.append(ca.path)
# Abandon any further tag removals to prevent any greater damage to archive
break
else:
success_count += 1
ca.load_cache(list(metadata_styles))
progdialog.hide()
QtCore.QCoreApplication.processEvents()
@ -1521,7 +1568,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.update_info_box()
self.update_menus()
summary = f"Successfully removed tags in {success_count} archive(s)."
summary = f"Successfully removed {success_count} tags in archive(s)."
if len(failed_list) > 0:
summary += f"\n\nThe remove operation failed in the following {len(failed_list)} archive(s):\n"
for f in failed_list:
@ -1538,9 +1585,13 @@ class TaggerWindow(QtWidgets.QMainWindow):
has_src_count = 0
src_style = self.load_data_style
dest_style = self.save_data_style
dest_styles = list(self.save_data_styles)
if src_style == dest_style:
# Remove the read style from the write style
if src_style in dest_styles:
dest_styles.remove(src_style)
if not dest_styles:
QtWidgets.QMessageBox.information(
self, "Copy Tags", "Can't copy tag style onto itself. Read style and modify style must be different."
)
@ -1551,7 +1602,9 @@ class TaggerWindow(QtWidgets.QMainWindow):
has_src_count += 1
if has_src_count == 0:
QtWidgets.QMessageBox.information(self, "Copy Tags", f"No archives with {src_style} tags selected!")
QtWidgets.QMessageBox.information(
self, "Copy Tags", f"No archives with {metadata_styles[src_style].name()} tags selected!"
)
return
if has_src_count != 0 and not self.dirty_flag_verification(
@ -1563,8 +1616,9 @@ class TaggerWindow(QtWidgets.QMainWindow):
reply = QtWidgets.QMessageBox.question(
self,
"Copy Tags",
f"Are you sure you wish to copy the {metadata_styles[src_style].name()}"
+ f" tags to {metadata_styles[dest_style].name()} tags in {has_src_count} archive(s)?",
f"Are you sure you wish to copy the {metadata_styles[src_style].name()} "
f"tags to {', '.join([metadata_styles[style].name() for style in dest_styles])} tags in "
f"{has_src_count} archive(s)?",
QtWidgets.QMessageBox.StandardButton.Yes,
QtWidgets.QMessageBox.StandardButton.No,
)
@ -1580,26 +1634,32 @@ class TaggerWindow(QtWidgets.QMainWindow):
failed_list = []
success_count = 0
for prog_idx, ca in enumerate(ca_list, 1):
QtCore.QCoreApplication.processEvents()
if prog_dialog.wasCanceled():
break
ca_saved = False
for style in dest_styles:
if ca.has_metadata(style):
QtCore.QCoreApplication.processEvents()
if prog_dialog.wasCanceled():
break
prog_dialog.setValue(prog_idx)
prog_dialog.setLabelText(str(ca.path))
QtCore.QCoreApplication.processEvents()
prog_dialog.setValue(prog_idx)
prog_dialog.setLabelText(str(ca.path))
center_window_on_parent(prog_dialog)
QtCore.QCoreApplication.processEvents()
if ca.has_metadata(src_style) and ca.is_writable():
md = ca.read_metadata(src_style)
if ca.has_metadata(src_style) and ca.is_writable():
md = ca.read_metadata(src_style)
if dest_style == "cbi" and self.config[0].Comic_Book_Lover__apply_transform_on_bulk_operation:
if style == "cbi" and self.config[0].Comic_Book_Lover__apply_transform_on_bulk_operation:
md = CBLTransformer(md, self.config[0]).apply()
if not ca.write_metadata(md, dest_style):
failed_list.append(ca.path)
else:
success_count += 1
if not ca.write_metadata(md, style):
failed_list.append(ca.path)
else:
if not ca_saved:
success_count += 1
ca_saved = True
ca.load_cache([self.load_data_style, self.save_data_style, src_style, dest_style])
ca.load_cache([self.load_data_style, self.save_data_style, src_style, style])
prog_dialog.hide()
QtCore.QCoreApplication.processEvents()
@ -1652,7 +1712,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
# read in metadata, and parse file name if not there
try:
md = ca.read_metadata(self.save_data_style)
md = ca.read_metadata(self.load_data_style)
except Exception as e:
md = GenericMetadata()
logger.error("Failed to load metadata for %s: %s", ca.path, e)
@ -1792,36 +1852,42 @@ class TaggerWindow(QtWidgets.QMainWindow):
if self.config[0].Issue_Identifier__auto_imprint:
md.fix_publisher()
if not ca.write_metadata(md, self.save_data_style):
match_results.write_failures.append(
Result(
Action.save,
Status.write_failure,
ca.path,
online_results=matches,
match_status=MatchStatus.good_match,
# Save each style
for style in self.save_data_styles:
if not ca.write_metadata(md, style):
match_results.write_failures.append(
Result(
Action.save,
Status.write_failure,
ca.path,
online_results=matches,
match_status=MatchStatus.good_match,
)
)
)
self.auto_tag_log("Save failed ;-(\n")
else:
match_results.good_matches.append(
Result(
Action.save,
Status.success,
ca.path,
online_results=matches,
match_status=MatchStatus.good_match,
self.auto_tag_log(
f"{metadata_styles[style].name()} save failed! Aborting any additional style saves.\n"
)
)
success = True
self.auto_tag_log("Save complete!\n")
ca.load_cache([self.load_data_style, self.save_data_style])
break
else:
match_results.good_matches.append(
Result(
Action.save,
Status.success,
ca.path,
online_results=matches,
match_status=MatchStatus.good_match,
)
)
success = True
self.auto_tag_log(f"{metadata_styles[style].name()} save complete!\n")
ca.load_cache([self.load_data_style] + self.save_data_styles)
return success, match_results
def auto_tag(self) -> None:
ca_list = self.fileSelectionList.get_selected_archive_list()
style = self.save_data_style
styles = self.save_data_styles
if len(ca_list) == 0:
QtWidgets.QMessageBox.information(self, "Auto-Tag", "No archives selected!")
@ -1837,7 +1903,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.config[0],
(
f"You have selected {len(ca_list)} archive(s) to automatically identify and write "
+ metadata_styles[style].name()
+ ", ".join([metadata_styles[style].name() for style in styles])
+ " tags to.\n\nPlease choose config below, and select OK to Auto-Tag."
),
)
@ -1864,7 +1930,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.auto_tag_log(f"Auto-Tagging {prog_idx} of {len(ca_list)}\n")
self.auto_tag_log(f"{ca.path}\n")
try:
cover_idx = ca.read_metadata(style).get_cover_page_index_list()[0]
cover_idx = ca.read_metadata(self.load_data_style).get_cover_page_index_list()[0]
except Exception as e:
cover_idx = 0
logger.error("Failed to load metadata for %s: %s", ca.path, e)
@ -1898,7 +1964,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.atprogdialog = None
summary = ""
summary += f"Successfully tagged archives: {len(match_results.good_matches)}\n"
summary += f"Successfully added {len(match_results.good_matches)} tags to archive(s)\n"
if len(match_results.multiple_matches) > 0:
summary += f"Archives with multiple matches: {len(match_results.multiple_matches)}\n"
@ -1934,7 +2000,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
matchdlg = AutoTagMatchWindow(
self,
match_results.multiple_matches,
style,
styles,
self.actual_issue_data_fetch,
self.config[0],
self.current_talker(),

View File

@ -0,0 +1,113 @@
"""Custom widgets"""
from __future__ import annotations
from typing import Any
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import QEvent, QRect, Qt, pyqtSignal
# Multiselect combobox from: https://gis.stackexchange.com/a/351152 (with custom changes)
class CheckableComboBox(QtWidgets.QComboBox):
itemChecked = pyqtSignal(str, bool)
def __init__(self, *args: Any, **kwargs: Any):
super().__init__(*args, **kwargs)
# Prevent popup from closing when clicking on an item
self.view().viewport().installEventFilter(self)
# Keeps track of when the combobox list is shown
self.justShown = False
def resizeEvent(self, event: Any) -> None:
# Recompute text to elide as needed
super().resizeEvent(event)
self._updateText()
def eventFilter(self, obj: Any, event: Any) -> bool:
# Allow events before the combobox list is shown
if obj == self.view().viewport():
# We record that the combobox list has been shown
if event.type() == QEvent.Show:
self.justShown = True
# We record that the combobox list has hidden,
# this will happen if the user does not make a selection
# but clicks outside of the combobox list or presses escape
if event.type() == QEvent.Hide:
self._updateText()
self.justShown = False
# QEvent.MouseButtonPress is inconsistent on activation because double clicks are a thing
if event.type() == QEvent.MouseButtonRelease:
# If self.justShown is true it means that they clicked on the combobox to change the checked items
# This is standard behavior (on macos) but I think it is surprising when it has a multiple select
if self.justShown:
self.justShown = False
return True
# Find the current index and item
index = self.view().indexAt(event.pos())
self.toggleItem(index.row())
return True
return False
def currentData(self) -> list[Any]:
# Return the list of all checked items data
res = []
for i in range(self.count()):
item = self.model().item(i)
if item.checkState() == Qt.Checked:
res.append(self.itemData(i))
return res
def addItem(self, text: str, data: Any = None) -> None:
super().addItem(text, data)
# Need to enable the checkboxes and require one checked item
# Expected that state of *all* checkboxes will be set ('adjust_save_style_combo' in taggerwindow.py)
if self.count() == 1:
self.model().item(0).setCheckState(Qt.CheckState.Checked)
def _updateText(self) -> None:
texts = []
for i in range(self.count()):
item = self.model().item(i)
if item.checkState() == Qt.Checked:
texts.append(item.text())
text = ", ".join(texts)
# Compute elided text (with "...")
# The QStyleOptionComboBox is needed for the call to subControlRect
so = QtWidgets.QStyleOptionComboBox()
# init with the current widget
so.initFrom(self)
# Ask the style for the size of the text field
rect = self.style().subControlRect(QtWidgets.QStyle.CC_ComboBox, so, QtWidgets.QStyle.SC_ComboBoxEditField)
# Compute the elided text
elidedText = self.fontMetrics().elidedText(text, Qt.ElideRight, rect.width())
# This CheckableComboBox does not use the index, so we clear it and set the placeholder text
self.setCurrentIndex(-1)
self.setPlaceholderText(elidedText)
def setItemChecked(self, index: Any, state: bool) -> None:
qt_state = Qt.Checked if state else Qt.Unchecked
item = self.model().item(index)
current = self.currentData()
# If we have at least one item checked emit itemChecked with the current check state and update text
# Require at least one item to be checked and provide a tooltip
if len(current) == 1 and not state and item.checkState() == Qt.Checked:
QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), self.toolTip(), self, QRect(), 3000)
return
if len(current) > 0:
item.setCheckState(qt_state)
self.itemChecked.emit(self.itemData(index), state)
self._updateText()
def toggleItem(self, index: int) -> None:
if self.model().item(index).checkState() == Qt.Checked:
self.setItemChecked(index, False)
else:
self.setItemChecked(index, True)

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>1096</width>
<height>621</height>
<height>658</height>
</rect>
</property>
<property name="sizePolicy">
@ -52,37 +52,23 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<layout class="QFormLayout" name="source_style_formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Read Style</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cbLoadDataStyle"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="saveStyleLabel">
<property name="text">
<string>Modify Style</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cbSaveDataStyle"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lbl_md_source">
<property name="text">
<string>Metadata Source</string>
<string>Data Source</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -825,9 +811,6 @@
<property name="text">
<string>Primary</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</column>
<column>
<property name="text">
@ -1166,7 +1149,7 @@
<x>0</x>
<y>0</y>
<width>1096</width>
<height>21</height>
<height>28</height>
</rect>
</property>
<widget class="QMenu" name="menuComicTagger">
@ -1383,7 +1366,7 @@
</action>
<action name="actionRemoveAuto">
<property name="text">
<string>Remove Current 'Modify' Tag Style</string>
<string>Remove Current 'Modify' Tag Style(s)</string>
</property>
</action>
<action name="actionRename">