diff --git a/comictaggerlib/issueselectionwindow.py b/comictaggerlib/issueselectionwindow.py index e76ba0d..0699215 100644 --- a/comictaggerlib/issueselectionwindow.py +++ b/comictaggerlib/issueselectionwindow.py @@ -143,6 +143,7 @@ class IssueSelectionWindow(SelectionWindow): self.accept() def update_row(self, row: int, issue: GenericMetadata) -> None: # type: ignore[override] + self.twList.setStyleSheet(self.twList.styleSheet()) item_text = issue.issue or "" item = self.twList.item(row, 0) item.setText(item_text) diff --git a/comictaggerlib/seriesselectionwindow.py b/comictaggerlib/seriesselectionwindow.py index 99489a3..dbdc769 100644 --- a/comictaggerlib/seriesselectionwindow.py +++ b/comictaggerlib/seriesselectionwindow.py @@ -23,7 +23,7 @@ from collections import deque import natsort from PyQt5 import QtCore, QtGui, QtWidgets, uic -from PyQt5.QtCore import QUrl, pyqtSignal +from PyQt5.QtCore import Qt, QUrl, pyqtSignal from comicapi import utils from comicapi.comicarchive import ComicArchive @@ -149,6 +149,7 @@ class SelectionWindow(QtWidgets.QDialog): literal: bool = False, ) -> None: super().__init__(parent) + self.setWindowModality(Qt.WindowModality.WindowModal) with self.ui_file.open(encoding="utf-8") as uifile: uic.loadUi(uifile, self) @@ -497,7 +498,6 @@ class SeriesSelectionWindow(SelectionWindow): selector = MatchSelectionWindow( self, issues, self.comic_archive, talker=self.talker, config=self.config ) - selector.setModal(True) selector.exec() if selector.result(): # we should now have a list index @@ -523,9 +523,8 @@ class SeriesSelectionWindow(SelectionWindow): break self.selector.setWindowTitle(title + "Select Issue") - self.selector.setModal(True) self.selector.finished.connect(self.issue_selected) - self.selector.open() + self.selector.show() def issue_selected(self, result) -> None: if result and self.selector: diff --git a/comictaggerlib/settingswindow.py b/comictaggerlib/settingswindow.py index cb13f98..e19266b 100644 --- a/comictaggerlib/settingswindow.py +++ b/comictaggerlib/settingswindow.py @@ -669,7 +669,6 @@ class SettingsWindow(QtWidgets.QDialog): def show_template_help(self) -> None: template_help_win = TemplateHelpWindow(self) - template_help_win.setModal(False) template_help_win.show() diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index 04cc2a3..6684d83 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -1090,8 +1090,7 @@ class TaggerWindow(QtWidgets.QMainWindow): self.selector.setWindowTitle(f"Search: '{series_name}' - Select Series") self.selector.finished.connect(self.finish_query) - self.selector.setModal(True) - self.selector.open() + self.selector.show() def finish_query(self, result) -> None: if result and self.selector: @@ -1154,20 +1153,22 @@ class TaggerWindow(QtWidgets.QMainWindow): self.metadata_to_form() def on_ratelimit(self, full_time: float, sleep_time: float) -> None: - toast = Toast(self) - # Convert to milliseconds, make it end half a second before the ratelimit triggers again, make sure we have a positive time - toast.setDuration(abs(int(sleep_time * 1000) - 500)) - toast.setResetDurationOnHover(False) - toast.setTitle("Rate Limit Hit!") - toast.setText( + self.toast = Toast(self) + if qtutils.is_dark_mode(): + self.toast.applyPreset(ToastPreset.WARNING_DARK) + else: + self.toast.applyPreset(ToastPreset.WARNING) + + # Convert to milliseconds, add 200ms because python is slow + self.toast.setDuration(abs(int(sleep_time * 1000) + 200)) + self.toast.setResetDurationOnHover(False) + self.toast.setFadeOutDuration(50) + self.toast.setTitle("Rate Limit Hit!") + self.toast.setText( f"Rate limit reached: {full_time:.0f}s until next request. Waiting {sleep_time:.0f}s for ratelimit" ) - if qtutils.is_dark_mode(): - toast.applyPreset(ToastPreset.WARNING_DARK) - else: - toast.applyPreset(ToastPreset.WARNING) - toast.setPositionRelativeToWidget(self) - toast.show() + self.toast.setPositionRelativeToWidget(self) + self.toast.show() def write_tags(self) -> None: if self.metadata is not None and self.comic_archive is not None: @@ -1395,7 +1396,6 @@ class TaggerWindow(QtWidgets.QMainWindow): def show_settings(self) -> None: settingswin = SettingsWindow(self, self.config, self.talkers) - settingswin.setModal(True) settingswin.exec() settingswin.result() self.adjust_source_combo() @@ -1779,8 +1779,7 @@ class TaggerWindow(QtWidgets.QMainWindow): return self.atprogdialog = AutoTagProgressWindow(self, self.current_talker()) - self.atprogdialog.setModal(True) - self.atprogdialog.show() + self.atprogdialog.open() self.atprogdialog.progressBar.setMaximum(len(ca_list)) self.atprogdialog.setWindowTitle("Auto-Tagging") @@ -1862,7 +1861,6 @@ class TaggerWindow(QtWidgets.QMainWindow): self.config[0], self.current_talker(), ) - matchdlg.setModal(True) matchdlg.exec() self.fileSelectionList.update_selected_rows() new_ca = self.fileSelectionList.get_current_archive() @@ -1980,7 +1978,6 @@ class TaggerWindow(QtWidgets.QMainWindow): "File Rename", "If you rename files now, unsaved data in the form will be lost. Are you sure?" ): dlg = RenameWindow(self, ca_list, self.selected_read_tags, self.config, self.talkers) - dlg.setModal(True) if dlg.exec() and self.comic_archive is not None: self.fileSelectionList.update_selected_rows() self.load_archive(self.comic_archive) diff --git a/comictaggerlib/ui/issueselectionwindow.ui b/comictaggerlib/ui/issueselectionwindow.ui index 2571374..4998283 100644 --- a/comictaggerlib/ui/issueselectionwindow.ui +++ b/comictaggerlib/ui/issueselectionwindow.ui @@ -49,6 +49,15 @@ 7 + + QTableWidget[rowCount="0"] { + background-image: url(":/graphics/about.png"); + background-attachment: fixed; + background-position: top center; + background-repeat: no-repeat; + background-color: white; +} + QAbstractItemView::SingleSelection diff --git a/comictaggerlib/ui/pyqttoast/constants.py b/comictaggerlib/ui/pyqttoast/constants.py index 17e921b..2a0e8ea 100644 --- a/comictaggerlib/ui/pyqttoast/constants.py +++ b/comictaggerlib/ui/pyqttoast/constants.py @@ -3,7 +3,6 @@ from __future__ import annotations from PyQt5.QtGui import QColor UPDATE_POSITION_DURATION = 200 -DURATION_BAR_UPDATE_INTERVAL = 1 DROP_SHADOW_SIZE = 5 SUCCESS_ACCENT_COLOR = QColor("#3E9141") WARNING_ACCENT_COLOR = QColor("#E8B849") @@ -23,7 +22,6 @@ DEFAULT_CLOSE_BUTTON_ICON_COLOR_DARK = QColor("#C9C9C9") __all__ = [ "UPDATE_POSITION_DURATION", - "DURATION_BAR_UPDATE_INTERVAL", "DROP_SHADOW_SIZE", "SUCCESS_ACCENT_COLOR", "WARNING_ACCENT_COLOR", diff --git a/comictaggerlib/ui/pyqttoast/toast.py b/comictaggerlib/ui/pyqttoast/toast.py index 15a4d29..8e28bd6 100644 --- a/comictaggerlib/ui/pyqttoast/toast.py +++ b/comictaggerlib/ui/pyqttoast/toast.py @@ -2,7 +2,18 @@ from __future__ import annotations import math -from PyQt5.QtCore import QEvent, QMargins, QPoint, QPropertyAnimation, QRect, QSize, Qt, QTimer, pyqtSignal +from PyQt5.QtCore import ( + QAbstractAnimation, + QEvent, + QMargins, + QPoint, + QPropertyAnimation, + QRect, + QSize, + Qt, + QTimer, + pyqtSignal, +) from PyQt5.QtGui import QColor, QFont, QFontMetrics, QGuiApplication, QIcon, QPixmap, QScreen from PyQt5.QtWidgets import QDialog, QGraphicsOpacityEffect, QLabel, QPushButton, QWidget @@ -19,7 +30,6 @@ from .constants import ( DEFAULT_TITLE_COLOR, DEFAULT_TITLE_COLOR_DARK, DROP_SHADOW_SIZE, - DURATION_BAR_UPDATE_INTERVAL, ERROR_ACCENT_COLOR, INFORMATION_ACCENT_COLOR, SUCCESS_ACCENT_COLOR, @@ -95,7 +105,6 @@ class Toast(QDialog): self.__close_button_margins = QMargins(0, -8, 0, -8) self.__text_section_spacing = 8 - self.__elapsed_time = 0 self.__fading_out = False self.__used = False @@ -145,7 +154,6 @@ class Toast(QDialog): # Duration bar chunk self.__duration_bar_chunk = QWidget(self.__duration_bar_container) - self.__duration_bar_chunk.setFixedHeight(20) self.__duration_bar_chunk.move(0, -16) # Set defaults @@ -173,10 +181,6 @@ class Toast(QDialog): self.__duration_timer.setSingleShot(True) self.__duration_timer.timeout.connect(self.hide) - # Timer for updating the duration bar - self.__duration_bar_timer = QTimer(self) - self.__duration_bar_timer.timeout.connect(self.__update_duration_bar) - # Apply stylesheet self.setStyleSheet((css_path / "toast.css").read_text(encoding="utf-8")) @@ -205,14 +209,13 @@ class Toast(QDialog): """ # Reset timer if hovered and resetting is enabled - if self.__duration != 0 and self.__duration_timer.isActive() and self.__reset_duration_on_hover: + if self.__reset_duration_on_hover and self.__duration != 0 and self.__duration_timer.isActive(): self.__duration_timer.stop() # Reset duration bar if enabled if self.__show_duration_bar: - self.__duration_bar_timer.stop() - self.__duration_bar_chunk.setFixedWidth(self.width()) - self.__elapsed_time = 0 + self.__bar_animation.stop() + self.__duration_bar_chunk.setGeometry(QRect(0, 0, self.__duration_bar_container.width(), 4)) def leaveEvent(self, event: QEvent) -> None: """Event that happens every time the mouse leaves this widget. @@ -222,12 +225,21 @@ class Toast(QDialog): """ # Start timer again when leaving notification and reset is enabled - if self.__duration != 0 and not self.__duration_timer.isActive() and self.__reset_duration_on_hover: + if self.__reset_duration_on_hover and self.__duration != 0 and not self.__duration_timer.isActive(): self.__duration_timer.start(self.__duration) # Restart duration bar animation if enabled if self.__show_duration_bar: - self.__duration_bar_timer.start(DURATION_BAR_UPDATE_INTERVAL) + self.__start_duration_bar_animation() + + def __start_duration_bar_animation(self) -> None: + self.__bar_animation = QPropertyAnimation(self.__duration_bar_chunk, b"geometry") + self.__bar_animation.setDuration(self.__duration) + self.__bar_animation.setStartValue(QRect(0, 0, self.__duration_bar.width(), 4)) + self.__bar_animation.setEndValue(QRect(0, 0, 0, 4)) + self.__bar_animation.setDirection(QAbstractAnimation.Direction.Forward) + + self.__bar_animation.start() def show(self) -> None: """Show the toast notification""" @@ -244,16 +256,12 @@ class Toast(QDialog): # Setup UI self.__setup_ui() + # Calculate position and show (animate position too if not first notification) + x, y = self.__calculate_position() # Start duration timer if self.__duration != 0: self.__duration_timer.start(self.__duration) - - # Start duration bar update timer - if self.__duration != 0 and self.__show_duration_bar: - self.__duration_bar_timer.start(DURATION_BAR_UPDATE_INTERVAL) - - # Calculate position and show (animate position too if not first notification) - x, y = self.__calculate_position() + self.__start_duration_bar_animation() # If not first toast on screen, also do a fade down/up animation if len(Toast.__currently_shown) > 1: @@ -327,7 +335,6 @@ class Toast(QDialog): if self in Toast.__currently_shown: Toast.__currently_shown.remove(self) - self.__elapsed_time = 0 self.__fading_out = False # Emit signal @@ -343,21 +350,6 @@ class Toast(QDialog): timer.timeout.connect(Toast.__show_next_in_queue) timer.start(self.__fade_in_duration) - def __update_duration_bar(self) -> None: - """Update the duration bar chunk with the elapsed time""" - - self.__elapsed_time += DURATION_BAR_UPDATE_INTERVAL - - if self.__elapsed_time >= self.__duration: - self.__duration_bar_timer.stop() - return - - new_chunk_width = math.floor( - self.__duration_bar_container.width() - - self.__elapsed_time / self.__duration * self.__duration_bar_container.width() - ) - self.__duration_bar_chunk.setFixedWidth(new_chunk_width) - def __update_position_xy(self, animate: bool = True) -> None: """Update the x and y position of the toast with an optional animation @@ -893,14 +885,11 @@ class Toast(QDialog): self.__close_button.setVisible(False) # Resize, move, and show duration bar if enabled - if self.__show_duration_bar: - self.__duration_bar_container.setFixedWidth(width) - self.__duration_bar_container.move(0, height - duration_bar_height) - self.__duration_bar.setFixedWidth(width) - self.__duration_bar_chunk.setFixedWidth(width) - self.__duration_bar_container.setVisible(True) - else: - self.__duration_bar_container.setVisible(False) + self.__duration_bar_container.setFixedWidth(width) + self.__duration_bar_container.move(0, height - duration_bar_height) + self.__duration_bar.setFixedWidth(width) + + self.__duration_bar_container.setVisible(self.__show_duration_bar) def __install_widget_event_filter(self) -> None: """Install an event filter on parent"""