Compare commits

..

7 Commits

Author SHA1 Message Date
81d89d7905 Only enable system tray when sending a notification because QT sucks
Some checks are pending
CI / lint (ubuntu-latest, 3.9) (push) Waiting to run
CI / build-and-test (macos-13, 3.13) (push) Waiting to run
CI / build-and-test (macos-13, 3.9) (push) Waiting to run
CI / build-and-test (macos-14, 3.13) (push) Waiting to run
CI / build-and-test (macos-14, 3.9) (push) Waiting to run
CI / build-and-test (ubuntu-22.04, 3.13) (push) Waiting to run
CI / build-and-test (ubuntu-22.04, 3.9) (push) Waiting to run
CI / build-and-test (ubuntu-22.04-arm, 3.13) (push) Waiting to run
CI / build-and-test (ubuntu-22.04-arm, 3.9) (push) Waiting to run
CI / build-and-test (windows-latest, 3.13) (push) Waiting to run
CI / build-and-test (windows-latest, 3.9) (push) Waiting to run
2025-09-21 17:58:31 -07:00
5faa6ecfe3 Use system notifications if available
Some checks failed
CI / lint (ubuntu-latest, 3.9) (push) Has been cancelled
CI / build-and-test (macos-13, 3.13) (push) Has been cancelled
CI / build-and-test (macos-13, 3.9) (push) Has been cancelled
CI / build-and-test (macos-14, 3.13) (push) Has been cancelled
CI / build-and-test (macos-14, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.9) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.13) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.9) (push) Has been cancelled
2025-09-21 16:45:14 -07:00
d01b90a88a Fix passing in parent
Some checks failed
CI / lint (ubuntu-latest, 3.9) (push) Has been cancelled
CI / build-and-test (macos-13, 3.13) (push) Has been cancelled
CI / build-and-test (macos-13, 3.9) (push) Has been cancelled
CI / build-and-test (macos-14, 3.13) (push) Has been cancelled
CI / build-and-test (macos-14, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.9) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.13) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.9) (push) Has been cancelled
2025-09-21 15:28:57 -07:00
88f11cd3b3 Fix ratelimit toast position
Some checks failed
CI / lint (ubuntu-latest, 3.9) (push) Has been cancelled
CI / build-and-test (macos-13, 3.13) (push) Has been cancelled
CI / build-and-test (macos-13, 3.9) (push) Has been cancelled
CI / build-and-test (macos-14, 3.13) (push) Has been cancelled
CI / build-and-test (macos-14, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.9) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.13) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.9) (push) Has been cancelled
2025-09-21 15:19:20 -07:00
e481fe5033 Fix credit window
Some checks failed
CI / lint (ubuntu-latest, 3.9) (push) Has been cancelled
CI / build-and-test (macos-13, 3.13) (push) Has been cancelled
CI / build-and-test (macos-13, 3.9) (push) Has been cancelled
CI / build-and-test (macos-14, 3.13) (push) Has been cancelled
CI / build-and-test (macos-14, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.13) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04-arm, 3.9) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.13) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.9) (push) Has been cancelled
2025-09-18 18:37:21 -07:00
9bc85d95a1 Fix some modal issues with dialogs 2025-09-18 16:31:24 -07:00
5256f016b7 Add links to issues and series 2025-09-18 16:30:51 -07:00
13 changed files with 207 additions and 104 deletions

View File

@ -138,6 +138,7 @@ class ComicSeries:
publisher: str
start_year: int | None
format: str | None
web_links: list[Url] = dataclasses.field(default_factory=list)
def copy(self) -> ComicSeries:
return copy.deepcopy(self)

View File

@ -18,7 +18,6 @@ from __future__ import annotations
import logging
import operator
from enum import Enum, auto
import natsort
from PyQt6 import QtCore, QtWidgets, uic
@ -32,15 +31,12 @@ from comictaggerlib.ui.qtutils import enable_widget
logger = logging.getLogger(__name__)
class EditMode(Enum):
EDIT = auto()
NEW = auto()
class CreditEditorWindow(QtWidgets.QDialog):
creditChanged = QtCore.pyqtSignal(Credit, int, EditMode)
creditChanged = QtCore.pyqtSignal(Credit, int)
def __init__(self, parent: QtWidgets.QWidget, tags: list[str], row: int, mode: EditMode, credit: Credit) -> None:
def __init__(
self, parent: QtWidgets.QWidget, tags: list[str], row: int, credit: Credit, title: str = "New Credit"
) -> None:
super().__init__(parent)
with (ui_path / "crediteditorwindow.ui").open(encoding="utf-8") as uifile:
@ -53,15 +49,11 @@ class CreditEditorWindow(QtWidgets.QDialog):
"credits.primary": self.cbPrimary,
}
self.mode = mode
self.credit = credit
self.row = row
self.tags = tags
if self.mode == EditMode.EDIT:
self.setWindowTitle("Edit Credit")
else:
self.setWindowTitle("New Credit")
self.setWindowTitle(title)
self.setModal(True)
# Add the entries to the role combobox
@ -129,4 +121,4 @@ class CreditEditorWindow(QtWidgets.QDialog):
QtWidgets.QDialog.accept(self)
new = self.get_credit()
if self.credit != new:
self.creditChanged.emit(new, self.row, self.mode)
self.creditChanged.emit(new, self.row)

View File

@ -10,6 +10,7 @@ import types
import settngs
from comictaggerlib.ctsettings import ct_ns
from comictaggerlib.ctversion import version
from comictaggerlib.graphics import graphics_path
from comictalker.comictalker import ComicTalker
@ -114,6 +115,9 @@ def open_tagger_window(
app.openFileRequest.connect(lambda x: config[0].Runtime_Options__files.append(x.toLocalFile()))
# The window Icon needs to be set here. It's also set in taggerwindow.ui but it doesn't seem to matter
app.setWindowIcon(QtGui.QIcon(":/graphics/app.png"))
app.setApplicationName("ComicTagger")
app.setApplicationDisplayName("ComicTagger")
app.setApplicationVersion(version)
if platform.system() == "Windows":
# For pure python, tell windows that we're not python,

View File

@ -40,18 +40,17 @@ class IssueNumberTableWidgetItem(QtWidgets.QTableWidgetItem):
class QueryThread(QtCore.QThread): # TODO: Evaluate thread semantics. Specifically with signals
finish = QtCore.pyqtSignal(list)
ratelimit = QtCore.pyqtSignal(float, float)
def __init__(
self,
talker: ComicTalker,
series_id: str,
finish: QtCore.pyqtSignal,
on_ratelimit: QtCore.pyqtSignal,
) -> None:
super().__init__()
self.series_id = series_id
self.talker = talker
self.finish = finish
self.on_ratelimit = on_ratelimit
def run(self) -> None:
@ -59,7 +58,7 @@ class QueryThread(QtCore.QThread): # TODO: Evaluate thread semantics. Specifica
issue_list = [
x
for x in self.talker.fetch_issues_in_series(
self.series_id, on_rate_limit=RLCallBack(lambda x, y: self.on_ratelimit.emit(x, y), 10)
self.series_id, on_rate_limit=RLCallBack(lambda x, y: self.ratelimit.emit(x, y), 10)
)
if x.issue_id is not None
]
@ -99,9 +98,9 @@ class IssueSelectionWindow(SelectionWindow):
self.querythread = QueryThread(
self.talker,
self.series_id,
self.finish,
self.ratelimit,
)
self.querythread.finish.connect(self.finish)
self.querythread.ratelimit.connect(self.ratelimit)
self.querythread.start()
def query_finished(self, issues: list[GenericMetadata]) -> None:
@ -183,4 +182,17 @@ class IssueSelectionWindow(SelectionWindow):
cover = issue._cover_image.URL if issue._cover_image else ""
self.cover_widget.set_issue_details(self.issue_id, [cover, *alt_images])
self.set_description(self.teDescription, issue.description or "")
series_link = ""
if issue.web_links:
url = (
issue.web_links[0]
.url.replace("&", "&")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("'", "&apos;")
.replace('"', "&quot;")
)
series_link = f'<a href="{url}">Link To Issue</a>'
self.lblIssueLink.setText(series_link)
return issue

View File

@ -98,6 +98,7 @@ class OptionalMessageDialog(QtWidgets.QDialog):
check_text: str = "",
) -> None:
d = OptionalMessageDialog(parent, StyleMessage, title, msg, checked=checked, check_text=check_text)
d.setModal(True)
def finished(i: int) -> None:
callback(d.theCheckBox.isChecked())
@ -117,6 +118,7 @@ class OptionalMessageDialog(QtWidgets.QDialog):
check_text: str = "",
) -> None:
d = OptionalMessageDialog(parent, StyleQuestion, title, msg, checked=checked, check_text=check_text)
d.setModal(True)
def finished(i: int) -> None:
callback(i == QtWidgets.QDialog.DialogCode.Accepted, d.theCheckBox.isChecked())
@ -130,6 +132,7 @@ class OptionalMessageDialog(QtWidgets.QDialog):
parent: QtWidgets.QWidget, title: str, msg: str, *, checked: bool = False, check_text: str = ""
) -> None:
d = OptionalMessageDialog(parent, StyleMessage, title, msg, checked=checked, check_text=check_text)
d.setModal(True)
d.theCheckBox.hide()
d.show()

View File

@ -405,6 +405,19 @@ class SeriesSelectionWindow(SelectionWindow):
except TalkerError:
pass
self.set_description(self.teDescription, series.description or "")
series_link = ""
if series.web_links:
url = (
series.web_links[0]
.url.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("'", "&apos;")
.replace('"', "&quot;")
)
series_link = f'<a href="{url}">Link To Series</a>'
self.lblSeriesLink.setText(series_link)
self.cover_widget.set_url(series.image_url)
return series
@ -473,7 +486,8 @@ class SeriesSelectionWindow(SelectionWindow):
if result == IIResult.single_good_match:
return self.update_match(issues[0])
qmsg = QtWidgets.QMessageBox(parent=self)
qmsg = QtWidgets.QMessageBox(parent=self.iddialog)
qmsg.setModal(False)
qmsg.setIcon(qmsg.Icon.Information)
qmsg.setText("Auto-Select Result")
qmsg.setInformativeText(" Manual interaction needed :-(")

View File

@ -47,7 +47,7 @@ from comictaggerlib.autotagprogresswindow import AutoTagProgressWindow, AutoTagT
from comictaggerlib.autotagstartwindow import AutoTagSettings, AutoTagStartWindow
from comictaggerlib.cbltransformer import CBLTransformer
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.crediteditorwindow import CreditEditorWindow, EditMode
from comictaggerlib.crediteditorwindow import CreditEditorWindow
from comictaggerlib.ctsettings import ct_ns
from comictaggerlib.exportwindow import ExportConfig, ExportConflictOpts, ExportWindow
from comictaggerlib.fileselectionlist import FileSelectionList
@ -249,7 +249,11 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.scrollAreaWidgetContents.adjustSize()
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
self.qicon = QtGui.QIcon(str(graphics_path / "app.png"))
self.setWindowIcon(self.qicon)
self.tray = QtWidgets.QSystemTrayIcon(self)
self.tray.show()
self.tray.hide() # QT doesn't initialize until you call show. on_ratelimit calls .show, .showMessage, and then .hide so that a tray item is not persistent. Specifically macOS will make an invisible tray icon if you keep it visible, even without an icon
# respect the command line selected tags
if config[0].Runtime_Options__tags_write:
@ -937,7 +941,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
for row, credit in enumerate(md.credits):
# if the role-person pair already exists, just skip adding it to the list
if self.is_dupe_credit(None, credit.role.title(), credit.person):
if self.get_dupe_credit(None, credit.role.title(), credit.person):
continue
self.add_new_credit_entry(row, credit)
@ -973,7 +977,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.twCredits.setItem(row, self.md_attributes["credits.primary"], item)
self.update_credit_primary_flag(row, credit.primary)
def is_dupe_credit(self, row: int | None, role: str, name: str) -> bool:
def get_dupe_credit(self, row: int | None, role: str, name: str) -> int:
for r in range(self.twCredits.rowCount()):
if r == row:
continue
@ -982,9 +986,9 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.twCredits.item(r, self.md_attributes["credits.role"]).text() == role
and self.twCredits.item(r, self.md_attributes["credits.person"]).text() == name
):
return True
return r
return False
return -1
def form_to_metadata(self) -> None:
# copy the data from the form into the metadata
@ -1206,7 +1210,18 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.metadata_to_form()
def on_ratelimit(self, full_time: float, sleep_time: float) -> None:
self.toast = Toast(QtWidgets.QApplication.activeWindow())
if QtWidgets.QSystemTrayIcon.supportsMessages():
self.tray.show()
self.tray.showMessage(
"Rate Limit Hit!",
f"Rate limit reached: {full_time:.0f}s until next request. Waiting {sleep_time:.0f}s for ratelimit",
self.qicon,
abs(int(sleep_time * 1000) + 200),
)
self.tray.hide()
return
self.toast = Toast(self)
# self.toast.__position_relative_to_widget = self
if qtutils.is_dark_mode():
self.toast.applyPreset(ToastPreset.WARNING_DARK)
else:
@ -1220,7 +1235,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.toast.setText(
f"Rate limit reached: {full_time:.0f}s until next request. Waiting {sleep_time:.0f}s for ratelimit"
)
self.toast.setPositionRelativeToWidget(self)
self.toast.setAlwaysOnMainScreen(True)
self.toast.show()
def write_tags(self) -> None:
@ -1337,15 +1352,29 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.page_list_editor.select_write_tags(self.selected_write_tags)
self.toggle_enable_embedding_hashes()
def cell_double_clicked(self, r: int, c: int) -> None:
self.edit_credit()
def add_credit(self) -> None:
self.modify_credits(False)
row = self.twCredits.rowCount()
editor = CreditEditorWindow(self, self.selected_write_tags, row, Credit())
editor.creditChanged.connect(self._credit_added)
editor.show()
def edit_credit(self) -> None:
if self.twCredits.currentRow() > -1:
self.modify_credits(True)
if self.twCredits.currentRow() < 0:
return
row = self.twCredits.currentRow()
lang = str(
self.twCredits.item(row, self.md_attributes["credits.language"]).data(QtCore.Qt.ItemDataRole.UserRole)
or utils.get_language_iso(self.twCredits.item(row, self.md_attributes["credits.language"]).text())
)
old = Credit(
self.twCredits.item(row, self.md_attributes["credits.person"]).text(),
self.twCredits.item(row, self.md_attributes["credits.role"]).text(),
self.twCredits.item(row, self.md_attributes["credits.primary"]).text() != "",
lang,
)
editor = CreditEditorWindow(self, self.selected_write_tags, row, old, "Edit Credit")
editor.creditChanged.connect(self._credit_changed)
editor.show()
def update_credit_primary_flag(self, row: int, primary: bool) -> None:
# if we're clearing a flag do it and quit
@ -1366,29 +1395,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
# Now set our new primary
self.twCredits.item(row, self.md_attributes["credits.primary"]).setText("Yes")
def modify_credits(self, edit: bool) -> None:
row = self.twCredits.rowCount()
old = Credit()
mode = EditMode.NEW
if edit:
mode = EditMode.EDIT
row = self.twCredits.currentRow()
lang = str(
self.twCredits.item(row, self.md_attributes["credits.language"]).data(QtCore.Qt.ItemDataRole.UserRole)
or utils.get_language_iso(self.twCredits.item(row, self.md_attributes["credits.language"]).text())
)
old = Credit(
self.twCredits.item(row, self.md_attributes["credits.person"]).text(),
self.twCredits.item(row, self.md_attributes["credits.role"]).text(),
self.twCredits.item(row, self.md_attributes["credits.primary"]).text() != "",
lang,
)
editor = CreditEditorWindow(self, self.selected_write_tags, row, mode, old)
editor.creditChanged.connect(self._credit_changed)
editor.show()
def _edit_credit(self, credit: Credit, row: int) -> None:
def _update_credit(self, credit: Credit, row: int) -> None:
assert isinstance(row, int)
lang = utils.get_language_from_iso(credit.language) or credit.language
self.twCredits.item(row, self.md_attributes["credits.role"]).setText(credit.role)
self.twCredits.item(row, self.md_attributes["credits.person"]).setText(credit.person)
@ -1409,26 +1417,44 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.update_credit_colors()
self.set_dirty_flag()
def _credit_changed(self, credit: Credit, row: int, mode: EditMode) -> None:
def _credit_changed(self, credit: Credit, row: int) -> None:
dupe_index = self.get_dupe_credit(row, credit.role, credit.person)
if dupe_index < 0:
return self._update_credit(credit, row)
# delete the dupe credit from list
qmsg = QtWidgets.QMessageBox(parent=self)
qmsg.setText("Duplicate Credit!")
qmsg.setInformativeText(
"This will create a duplicate credit entry. Would you like to merge the entries, or create a duplicate?"
)
qmsg.addButton("Merge", QtWidgets.QMessageBox.ButtonRole.AcceptRole)
qmsg.addButton("Duplicate", QtWidgets.QMessageBox.ButtonRole.RejectRole)
if self.is_dupe_credit(row, credit.role, credit.person):
# delete the dupe credit from list
qmsg = QtWidgets.QMessageBox()
qmsg.setText("Duplicate Credit!")
qmsg.setInformativeText(
"This will create a duplicate credit entry. Would you like to merge the entries, or create a duplicate?"
)
qmsg.addButton("Merge", QtWidgets.QMessageBox.ButtonRole.AcceptRole)
qmsg.addButton("Duplicate", QtWidgets.QMessageBox.ButtonRole.NoRole)
def _merge(credit: Credit, row: int, existing: int) -> None:
self.twCredits.removeRow(row)
self._update_credit(credit, existing)
if qmsg.exec() == 0 and mode == EditMode.EDIT:
# just remove the row that would be same
self.twCredits.removeRow(row)
mode = EditMode.NEW
qmsg.accepted.connect(functools.partial(_merge, credit, row, dupe_index))
qmsg.rejected.connect(functools.partial(self._update_credit, credit, row))
if mode == EditMode.EDIT:
return self._edit_credit(credit, row)
self._add_credit(credit)
qmsg.show()
def _credit_added(self, credit: Credit) -> None:
dupe_index = self.get_dupe_credit(None, credit.role, credit.person)
if dupe_index < 0:
self._add_credit(credit)
return
# delete the dupe credit from list
qmsg = QtWidgets.QMessageBox(parent=self)
qmsg.setText("Duplicate Credit!")
qmsg.setInformativeText(
"This will create a duplicate credit entry. Would you like to merge the entries, or create a duplicate?"
)
qmsg.addButton("Merge", QtWidgets.QMessageBox.ButtonRole.AcceptRole)
qmsg.addButton("Duplicate", QtWidgets.QMessageBox.ButtonRole.RejectRole)
qmsg.accepted.connect(functools.partial(self._update_credit, credit, dupe_index))
qmsg.rejected.connect(functools.partial(self._add_credit, credit))
qmsg.show()
def remove_credit(self) -> None:
row = self.twCredits.currentRow()
@ -1940,26 +1966,26 @@ class TaggerWindow(QtWidgets.QMainWindow):
errorbox.open()
def dirty_flag_verification(self, title: str, desc: str) -> bool:
if self.dirty_flag:
reply = QtWidgets.QMessageBox.question(
self,
title,
desc,
(
QtWidgets.QMessageBox.StandardButton.Save
| QtWidgets.QMessageBox.StandardButton.Cancel
| QtWidgets.QMessageBox.StandardButton.Discard
),
QtWidgets.QMessageBox.StandardButton.Cancel,
)
if not self.dirty_flag:
return True
reply = QtWidgets.QMessageBox.question(
self,
title,
desc,
(
QtWidgets.QMessageBox.StandardButton.Save
| QtWidgets.QMessageBox.StandardButton.Cancel
| QtWidgets.QMessageBox.StandardButton.Discard
),
QtWidgets.QMessageBox.StandardButton.Cancel,
)
if reply == QtWidgets.QMessageBox.StandardButton.Discard:
return True
if reply == QtWidgets.QMessageBox.StandardButton.Save:
self.write_tags()
return True
return False
return True
if reply == QtWidgets.QMessageBox.StandardButton.Discard:
return True
if reply == QtWidgets.QMessageBox.StandardButton.Save:
self.write_tags()
return True
return False
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
if self.dirty_flag_verification(

View File

@ -161,6 +161,25 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblIssueLink">
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Issue Link:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="imageSourceLogo" native="true">
<property name="minimumSize">

View File

@ -1324,14 +1324,14 @@ class Toast(QDialog):
if on:
self.setWindowFlags(
Qt.WindowType.Tool
Qt.WindowType.Popup
| Qt.WindowType.CustomizeWindowHint
| Qt.WindowType.FramelessWindowHint
| Qt.WindowType.WindowStaysOnTopHint
)
else:
self.setWindowFlags(
Qt.WindowType.Tool | Qt.WindowType.CustomizeWindowHint | Qt.WindowType.FramelessWindowHint
Qt.WindowType.Popup | Qt.WindowType.CustomizeWindowHint | Qt.WindowType.FramelessWindowHint
)
def getBorderRadius(self) -> int:

View File

@ -238,21 +238,21 @@ try:
return new_widget
def critical(parent: QWidget | None, title: str, text: str) -> None:
qmsg = QtWidgets.QMessageBox(parent)
qmsg = QtWidgets.QMessageBox(parent=parent)
qmsg.setIcon(qmsg.Icon.Critical)
qmsg.setText(title)
qmsg.setInformativeText(text)
return qmsg.show()
def warning(parent: QWidget | None, title: str, text: str) -> None:
qmsg = QtWidgets.QMessageBox(parent)
qmsg = QtWidgets.QMessageBox(parent=parent)
qmsg.setIcon(qmsg.Icon.Warning)
qmsg.setText(title)
qmsg.setInformativeText(text)
return qmsg.show()
def information(parent: QWidget | None, title: str, text: str) -> None:
qmsg = QtWidgets.QMessageBox(parent)
qmsg = QtWidgets.QMessageBox(parent=parent)
qmsg.setIcon(qmsg.Icon.Information)
qmsg.setText(title)
qmsg.setInformativeText(text)

View File

@ -90,6 +90,31 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblSeriesLink">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Series Link:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="imageSourceLogo" native="true">
<property name="minimumSize">

View File

@ -77,9 +77,9 @@ def generate_api_widgets(
def call_check_api(*args: Any, tab: TalkerTab, talker: ComicTalker, definitions: settngs.Definitions) -> None:
check_text, check_bool = talker.check_status(get_config_from_tab(tab, definitions[group_for_plugin(talker)]))
if check_bool:
return qtutils.information(TalkerTab.tab, "API Test Success", check_text)
return qtutils.information(tab.tab, "API Test Success", check_text)
qtutils.warning(TalkerTab.tab, "API Test Failed", check_text)
qtutils.warning(tab.tab, "API Test Failed", check_text)
# get the actual config objects in case they have overwritten the default
btn_test_row = None

View File

@ -302,7 +302,7 @@ class ComicVineTalker(ComicTalker):
"format": "json",
"resources": "volume",
"query": search_series_name,
"field_list": "volume,name,id,start_year,publisher,image,description,count_of_issues,aliases",
"field_list": "volume,name,id,start_year,publisher,image,description,count_of_issues,aliases,site_detail_url",
"page": 1,
"limit": 100,
}
@ -801,7 +801,7 @@ class ComicVineTalker(ComicTalker):
aliases = record.get("aliases") or ""
return ComicSeries(
series = ComicSeries(
aliases=set(utils.split(aliases, "\n")),
count_of_issues=record.get("count_of_issues"),
count_of_volumes=None,
@ -813,6 +813,13 @@ class ComicVineTalker(ComicTalker):
start_year=start_year,
format=None,
)
url = utils.xlate(record.get("site_detail_url"))
if url:
try:
series.web_links = [parse_url(url)]
except LocationParseError:
...
return series
def _fetch_issues_in_series(
self,