Merge branch 'mizaki-meta_multi' into develop
This commit is contained in:
commit
1e4a3b2484
@ -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!")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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(),
|
||||
|
114
comictaggerlib/ui/customwidgets.py
Normal file
114
comictaggerlib/ui/customwidgets.py
Normal 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)
|
@ -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 "short" 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">
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user