Merge branch 'mizaki-meta_multi' into develop

This commit is contained in:
Timmy Welch 2024-02-12 17:29:45 -08:00
commit 1e4a3b2484
9 changed files with 368 additions and 147 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,15 @@ 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",
f"Saving {metadata_styles[style].name()} the tags to the archive seemed to fail!",
)
break
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

@ -18,12 +18,13 @@ def general(parser: settngs.Manager) -> None:
action=argparse.BooleanOptionalAction,
help="Disable the ComicRack metadata type",
)
parser.add_setting("use_short_metadata_names", default=False, action=argparse.BooleanOptionalAction, cmdline=False)
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 +256,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
@ -97,6 +97,7 @@ class settngs_namespace(settngs.TypedNS):
General__check_for_new_version: bool
General__disable_cr: bool
General__use_short_metadata_names: bool
Dialog_Flags__show_disclaimer: bool
Dialog_Flags__dont_notify_about_this_version: str

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,15 @@ 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 = set()
for style in styles:
enabled_widgets.update(style.supported_attributes)
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

@ -376,6 +376,7 @@ class SettingsWindow(QtWidgets.QDialog):
self.tePublisherFilter.setPlainText("\n".join(self.config[0].Issue_Identifier__publisher_filter))
self.cbxCheckForNewVersion.setChecked(self.config[0].General__check_for_new_version)
self.cbxShortMetadataNames.setChecked(self.config[0].General__use_short_metadata_names)
self.cbxComplicatedParser.setChecked(self.config[0].Filename_Parsing__complicated_parser)
self.cbxRemoveC2C.setChecked(self.config[0].Filename_Parsing__remove_c2c)
@ -493,6 +494,12 @@ class SettingsWindow(QtWidgets.QDialog):
self.config[0].General__check_for_new_version = self.cbxCheckForNewVersion.isChecked()
# Update metadata style names if required
if self.cbxShortMetadataNames.isChecked() != self.config[0].General__use_short_metadata_names:
self.config[0].General__use_short_metadata_names = self.cbxShortMetadataNames.isChecked()
self.parent().populate_style_names()
self.parent().adjust_save_style_combo()
self.config[0].Issue_Identifier__series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value()
self.config[0].Issue_Identifier__series_match_search_thresh = self.sbNameMatchSearchThresh.value()
self.config[0].Issue_Identifier__publisher_filter = utils.split(self.tePublisherFilter.toPlainText(), "\n")

View File

@ -215,15 +215,16 @@ 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
self.setAcceptDrops(True)
self.view_tag_actions, self.remove_tag_actions = self.tag_actions()
@ -273,7 +274,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 +439,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 +1147,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 +1156,23 @@ 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)
failed_style: str = ""
# Save each style
for style in self.save_data_styles:
success = self.comic_archive.write_metadata(self.metadata, style)
if not success:
failed_style = metadata_styles[style].name()
break
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 failed_style:
QtWidgets.QMessageBox.warning(
self,
"Save failed",
f"The tag save operation seemed to fail for: {failed_style}",
)
else:
self.clear_dirty_flag()
self.update_info_box()
@ -1186,9 +1198,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 +1208,16 @@ 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 = set()
for style in styles:
enabled.update(style.supported_attributes)
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 +1227,16 @@ class TaggerWindow(QtWidgets.QMainWindow):
def update_metadata_style_tweaks(self) -> None:
# depending on the current data style, certain fields are disabled
enabled_widgets = set()
for style in self.save_data_styles:
enabled_widgets.update(metadata_styles[style].supported_attributes)
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,14 +1370,26 @@ 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:
def populate_style_names(self) -> None:
# First clear all entries (called from settingswindow.py)
self.cbSaveDataStyle.clear()
# Add the entries to the tag style combobox
for style in metadata_styles.values():
self.cbLoadDataStyle.addItem(style.name(), style.short_name)
self.cbSaveDataStyle.addItem(style.name(), style.short_name)
if self.config[0].General__use_short_metadata_names:
self.cbSaveDataStyle.addItem(style.short_name.upper(), style.short_name)
else:
self.cbSaveDataStyle.addItem(style.name(), style.short_name)
def populate_combo_boxes(self) -> None:
self.populate_style_names()
self.adjust_load_style_combo()
self.adjust_save_style_combo()
@ -1461,19 +1490,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 +1522,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 +1544,16 @@ 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 ca.remove_metadata(style):
success_count += 1
else:
failed_list.append(ca.path)
# Abandon any further tag removals to prevent any greater damage to archive
break
ca.reset_cache()
ca.load_cache(list(metadata_styles))
progdialog.hide()
QtCore.QCoreApplication.processEvents()
@ -1521,7 +1561,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 +1578,13 @@ class TaggerWindow(QtWidgets.QMainWindow):
has_src_count = 0
src_style = self.load_data_style
dest_style = self.save_data_style
dest_styles = 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 +1595,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 +1609,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 +1627,36 @@ 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
prog_dialog.setValue(prog_idx)
prog_dialog.setLabelText(str(ca.path))
QtCore.QCoreApplication.processEvents()
ca_saved = False
if ca.has_metadata(src_style) and ca.is_writable():
md = ca.read_metadata(src_style)
else:
continue
if dest_style == "cbi" and self.config[0].Comic_Book_Lover__apply_transform_on_bulk_operation:
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))
center_window_on_parent(prog_dialog)
QtCore.QCoreApplication.processEvents()
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)
if ca.write_metadata(md, style):
if not ca_saved:
success_count += 1
ca_saved = True
else:
success_count += 1
failed_list.append(ca.path)
ca.load_cache([self.load_data_style, self.save_data_style, src_style, dest_style])
ca.reset_cache()
ca.load_cache([self.load_data_style, *self.save_data_styles])
prog_dialog.hide()
QtCore.QCoreApplication.processEvents()
@ -1647,12 +1704,22 @@ class TaggerWindow(QtWidgets.QMainWindow):
def identify_and_tag_single_archive(
self, ca: ComicArchive, match_results: OnlineMatchResults, dlg: AutoTagStartWindow
) -> tuple[bool, OnlineMatchResults]:
def metadata_save() -> bool:
for style in self.save_data_styles:
# write out the new data
if not ca.write_metadata(md, style):
self.auto_tag_log(
f"{metadata_styles[style].name()} save failed! Aborting any additional style saves.\n"
)
return False
return True
success = False
ii = IssueIdentifier(ca, self.config[0], self.current_talker())
# 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 +1859,33 @@ 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,
)
)
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,
)
)
res = Result(
Action.save,
status=Status.success,
original_path=ca.path,
online_results=matches,
match_status=MatchStatus.good_match,
md=md,
tags_written=self.save_data_styles,
)
# Save styles
if metadata_save():
match_results.good_matches.append(res)
success = True
self.auto_tag_log("Save complete!\n")
ca.load_cache([self.load_data_style, self.save_data_style])
else:
res.status = Status.write_failure
match_results.write_failures.append(res)
ca.reset_cache()
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 +1901,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 +1928,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 +1962,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.atprogdialog = None
summary = ""
summary += f"Successfully tagged archives: {len(match_results.good_matches)}\n"
summary += f"Successfully added {', '.join([metadata_styles[style].name() for style in self.save_data_styles])} tags to {len(match_results.good_matches)} archive(s)\n"
if len(match_results.multiple_matches) > 0:
summary += f"Archives with multiple matches: {len(match_results.multiple_matches)}\n"
@ -1934,7 +1998,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,114 @@
"""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

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>702</width>
<height>559</height>
<width>703</width>
<height>574</height>
</rect>
</property>
<property name="windowTitle">
@ -41,7 +41,7 @@
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<item row="2" column="0">
<widget class="QPushButton" name="btnResetSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -54,23 +54,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblDefaultSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Revert to default settings</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QPushButton" name="btnClearCache">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -83,7 +67,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -99,6 +83,22 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblDefaultSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Revert to default settings</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="cbxCheckForNewVersion">
<property name="text">
@ -106,6 +106,16 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="cbxShortMetadataNames">
<property name="toolTip">
<string>Use the short name for the metadata styles (CBI, CR, etc.)</string>
</property>
<property name="text">
<string>Use &quot;short&quot; names for metadata styles</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -411,7 +421,7 @@
<x>11</x>
<y>21</y>
<width>251</width>
<height>199</height>
<height>206</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_7">

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,43 +52,49 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<layout class="QFormLayout" name="style_layout">
<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>
<item row="0" column="1">
<widget class="QComboBox" name="cbx_sources"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cbLoadDataStyle"/>
</item>
<item row="2" column="1">
<widget class="CheckableComboBox" name="cbSaveDataStyle"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbl_read_style">
<property name="text">
<string>Read Style</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_modify_style">
<property name="text">
<string>Modify Styles</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -825,9 +831,6 @@
<property name="text">
<string>Primary</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</column>
<column>
<property name="text">
@ -1166,7 +1169,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 +1386,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">
@ -1458,6 +1461,13 @@
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>CheckableComboBox</class>
<extends>QComboBox</extends>
<header>comictaggerlib.ui.customwidgets</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>