diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index 61e2a9b..374a1c5 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -23,8 +23,6 @@ jobs: - name: Install dependencies run: | - # pyside 6.6.3 has some issue in their .pyi files - pip install PySide6==6.6.2 pip install -r requirements.txt pip install mypy==1.10.0 mkdir tagstudio/.mypy_cache diff --git a/requirements.txt b/requirements.txt index 7a1b5d5..a353c70 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,12 @@ humanfriendly==10.0 opencv_python>=4.8.0.74,<=4.9.0.80 Pillow==10.3.0 -PySide6>=6.5.1.1,<=6.6.3.1 -PySide6_Addons>=6.5.1.1,<=6.6.3.1 -PySide6_Essentials>=6.5.1.1,<=6.6.3.1 +PySide6==6.7.1 +PySide6_Addons==6.7.1 +PySide6_Essentials==6.7.1 typing_extensions>=3.10.0.0,<=4.11.0 ujson>=5.8.0,<=5.9.0 +numpy==1.26.4 rawpy==0.21.0 pillow-heif==0.16.0 chardet==5.2.0 diff --git a/tagstudio/src/qt/helpers/qbutton_wrapper.py b/tagstudio/src/qt/helpers/qbutton_wrapper.py new file mode 100644 index 0000000..ea55444 --- /dev/null +++ b/tagstudio/src/qt/helpers/qbutton_wrapper.py @@ -0,0 +1,16 @@ +# Copyright (C) 2024 Travis Abendshien (CyanVoxel). +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + +from PySide6.QtWidgets import QPushButton + + +class QPushButtonWrapper(QPushButton): + """ + This is a customized implementation of the PySide6 QPushButton that allows to suppress the warning that is triggered + by disconnecting a signal that is not currently connected. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.is_connected = False diff --git a/tagstudio/src/qt/modals/add_field.py b/tagstudio/src/qt/modals/add_field.py index f315b86..c0137da 100644 --- a/tagstudio/src/qt/modals/add_field.py +++ b/tagstudio/src/qt/modals/add_field.py @@ -24,6 +24,7 @@ class AddFieldModal(QWidget): # - OR - # [Cancel] [Save] super().__init__() + self.is_connected = False self.lib = library self.setWindowTitle(f"Add Field") self.setWindowModality(Qt.WindowModality.ApplicationModal) diff --git a/tagstudio/src/qt/pagination.py b/tagstudio/src/qt/pagination.py index 8d46b8e..904dd19 100644 --- a/tagstudio/src/qt/pagination.py +++ b/tagstudio/src/qt/pagination.py @@ -15,7 +15,7 @@ from PySide6.QtWidgets import ( QLineEdit, QSizePolicy, ) - +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper # class NumberEdit(QLineEdit): # def __init__(self, parent=None) -> None: @@ -50,13 +50,13 @@ class Pagination(QWidget, QObject): # self.setMinimumHeight(32) # [<] ---------------------------------- - self.prev_button = QPushButton() + self.prev_button = QPushButtonWrapper() self.prev_button.setText("<") self.prev_button.setMinimumSize(self.button_size) self.prev_button.setMaximumSize(self.button_size) # --- [1] ------------------------------ - self.start_button = QPushButton() + self.start_button = QPushButtonWrapper() self.start_button.setMinimumSize(self.button_size) self.start_button.setMaximumSize(self.button_size) # self.start_button.setStyleSheet('background:cyan;') @@ -104,14 +104,14 @@ class Pagination(QWidget, QObject): self.end_ellipses.setText(". . .") # ----------------------------- [42] --- - self.end_button = QPushButton() + self.end_button = QPushButtonWrapper() self.end_button.setMinimumSize(self.button_size) self.end_button.setMaximumSize(self.button_size) # self.end_button.setMaximumHeight(self.button_size.height()) # self.end_button.setStyleSheet('background:red;') # ---------------------------------- [>] - self.next_button = QPushButton() + self.next_button = QPushButtonWrapper() self.next_button.setText(">") self.next_button.setMinimumSize(self.button_size) self.next_button.setMaximumSize(self.button_size) @@ -428,16 +428,15 @@ class Pagination(QWidget, QObject): # print(f'GOTO PAGE: {index}') self.update_buttons(self.page_count, index) - def _assign_click(self, button: QPushButton, index): - try: + def _assign_click(self, button: QPushButtonWrapper, index): + if button.is_connected: button.clicked.disconnect() - except RuntimeError: - pass button.clicked.connect(lambda checked=False, i=index: self._goto_page(i)) + button.is_connected = True def _populate_buffer_buttons(self): for i in range(max(self.buffer_page_count * 2, 5)): - button = QPushButton() + button = QPushButtonWrapper() button.setMinimumSize(self.button_size) button.setMaximumSize(self.button_size) button.setHidden(True) @@ -445,7 +444,7 @@ class Pagination(QWidget, QObject): self.start_buffer_layout.addWidget(button) for i in range(max(self.buffer_page_count * 2, 5)): - button = QPushButton() + button = QPushButtonWrapper() button.setMinimumSize(self.button_size) button.setMaximumSize(self.button_size) button.setHidden(True) diff --git a/tagstudio/src/qt/widgets/fields.py b/tagstudio/src/qt/widgets/fields.py index dfef94e..355a0fa 100644 --- a/tagstudio/src/qt/widgets/fields.py +++ b/tagstudio/src/qt/widgets/fields.py @@ -13,6 +13,7 @@ from PIL import Image, ImageQt from PySide6.QtCore import Qt, QEvent from PySide6.QtGui import QPixmap, QEnterEvent from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper class FieldContainer(QWidget): @@ -81,7 +82,7 @@ class FieldContainer(QWidget): self.title_layout.addStretch(2) - self.copy_button = QPushButton() + self.copy_button = QPushButtonWrapper() self.copy_button.setMinimumSize(button_size, button_size) self.copy_button.setMaximumSize(button_size, button_size) self.copy_button.setFlat(True) @@ -92,7 +93,7 @@ class FieldContainer(QWidget): self.title_layout.addWidget(self.copy_button) self.copy_button.setHidden(True) - self.edit_button = QPushButton() + self.edit_button = QPushButtonWrapper() self.edit_button.setMinimumSize(button_size, button_size) self.edit_button.setMaximumSize(button_size, button_size) self.edit_button.setFlat(True) @@ -101,7 +102,7 @@ class FieldContainer(QWidget): self.title_layout.addWidget(self.edit_button) self.edit_button.setHidden(True) - self.remove_button = QPushButton() + self.remove_button = QPushButtonWrapper() self.remove_button.setMinimumSize(button_size, button_size) self.remove_button.setMaximumSize(button_size, button_size) self.remove_button.setFlat(True) @@ -124,31 +125,30 @@ class FieldContainer(QWidget): # self.set_inner_widget(mode) def set_copy_callback(self, callback: Optional[MethodType]): - try: + if self.copy_button.is_connected: self.copy_button.clicked.disconnect() - except RuntimeError: - pass self.copy_callback = callback self.copy_button.clicked.connect(callback) + if callback is not None: + self.copy_button.is_connected = True def set_edit_callback(self, callback: Optional[MethodType]): - try: + if self.edit_button.is_connected: self.edit_button.clicked.disconnect() - except RuntimeError: - pass self.edit_callback = callback self.edit_button.clicked.connect(callback) + if callback is not None: + self.edit_button.is_connected = True def set_remove_callback(self, callback: Optional[Callable]): - try: + if self.remove_button.is_connected: self.remove_button.clicked.disconnect() - except RuntimeError: - pass self.remove_callback = callback self.remove_button.clicked.connect(callback) + self.remove_button.is_connected = True def set_inner_widget(self, widget: "FieldWidget"): # widget.setStyleSheet('background-color:green;') diff --git a/tagstudio/src/qt/widgets/item_thumb.py b/tagstudio/src/qt/widgets/item_thumb.py index 82c72f4..5be2e3f 100644 --- a/tagstudio/src/qt/widgets/item_thumb.py +++ b/tagstudio/src/qt/widgets/item_thumb.py @@ -1,8 +1,7 @@ # Copyright (C) 2024 Travis Abendshien (CyanVoxel). # Licensed under the GPL-3.0 License. # Created for TagStudio: https://github.com/CyanVoxel/TagStudio - - +import contextlib import logging import os import time @@ -396,12 +395,11 @@ class ItemThumb(FlowWidget): def update_clickable(self, clickable: typing.Callable): """Updates attributes of a thumbnail element.""" # logging.info(f'[GUI] Updating Click Event for element {id(element)}: {id(clickable) if clickable else None}') - try: + if self.thumb_button.is_connected: self.thumb_button.clicked.disconnect() - except RuntimeError: - pass if clickable: self.thumb_button.clicked.connect(clickable) + self.thumb_button.is_connected = True def update_badges(self): if self.mode == ItemType.ENTRY: diff --git a/tagstudio/src/qt/widgets/preview_panel.py b/tagstudio/src/qt/widgets/preview_panel.py index 18289c4..4096c0d 100644 --- a/tagstudio/src/qt/widgets/preview_panel.py +++ b/tagstudio/src/qt/widgets/preview_panel.py @@ -40,6 +40,7 @@ from src.qt.widgets.text import TextWidget from src.qt.widgets.panel import PanelModal from src.qt.widgets.text_box_edit import EditTextBox from src.qt.widgets.text_line_edit import EditTextLine +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper from src.qt.widgets.video_player import VideoPlayer @@ -61,6 +62,7 @@ class PreviewPanel(QWidget): def __init__(self, library: Library, driver: "QtDriver"): super().__init__() + self.is_connected = False self.lib = library self.driver: QtDriver = driver self.initialized = False @@ -83,7 +85,7 @@ class PreviewPanel(QWidget): self.open_file_action = QAction("Open file", self) self.open_explorer_action = QAction("Open file in explorer", self) - self.preview_img = QPushButton() + self.preview_img = QPushButtonWrapper() self.preview_img.setMinimumSize(*self.img_button_size) self.preview_img.setFlat(True) self.preview_img.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu) @@ -218,7 +220,7 @@ class PreviewPanel(QWidget): self.afb_layout = QVBoxLayout(self.afb_container) self.afb_layout.setContentsMargins(0, 12, 0, 0) - self.add_field_button = QPushButton() + self.add_field_button = QPushButtonWrapper() self.add_field_button.setCursor(Qt.CursorShape.PointingHandCursor) self.add_field_button.setMinimumSize(96, 28) self.add_field_button.setMaximumSize(96, 28) @@ -279,7 +281,9 @@ class PreviewPanel(QWidget): row_layout.addWidget(label) layout.addLayout(row_layout) - def set_button_style(btn: QPushButton, extras: list[str] | None = None): + def set_button_style( + btn: QPushButtonWrapper | QPushButton, extras: list[str] | None = None + ): base_style = [ f"background-color:{Theme.COLOR_BG.value};", "border-radius:6px;", @@ -317,7 +321,6 @@ class PreviewPanel(QWidget): button.clicked.connect(open_library_button_clicked(full_val)) set_button_style(button) - button_remove = QPushButton("➖") button_remove.setCursor(Qt.CursorShape.PointingHandCursor) button_remove.setFixedWidth(30) @@ -411,16 +414,16 @@ class PreviewPanel(QWidget): self.afb_container, Qt.AlignmentFlag.AlignHCenter ) - try: + if self.afm.is_connected: self.afm.done.disconnect() + if self.add_field_button.is_connected: self.add_field_button.clicked.disconnect() - except RuntimeError: - pass # self.afm.done.connect(lambda f: (self.lib.add_field_to_entry(self.selected[0][1], f), self.update_widgets())) self.afm.done.connect( lambda f: (self.add_field_to_selected(f), self.update_widgets()) ) + self.afm.is_connected = True self.add_field_button.clicked.connect(self.afm.show) def add_field_to_selected(self, field_id: int): @@ -466,10 +469,8 @@ class PreviewPanel(QWidget): True, update_on_ratio_change=True, ) - try: + if self.preview_img.is_connected: self.preview_img.clicked.disconnect() - except RuntimeError: - pass for i, c in enumerate(self.containers): c.setHidden(True) self.preview_img.show() @@ -588,14 +589,12 @@ class PreviewPanel(QWidget): f"[PreviewPanel][ERROR] Couldn't Render thumbnail for {filepath} (because of {e})" ) - try: + if self.preview_img.is_connected: self.preview_img.clicked.disconnect() - except RuntimeError: - pass self.preview_img.clicked.connect( lambda checked=False, filepath=filepath: open_file(filepath) ) - + self.preview_img.is_connected = True self.selected = list(self.driver.selected) for i, f in enumerate(item.fields): self.write_container(i, f) @@ -641,10 +640,8 @@ class PreviewPanel(QWidget): True, update_on_ratio_change=True, ) - try: + if self.preview_img.is_connected: self.preview_img.clicked.disconnect() - except RuntimeError: - pass self.common_fields = [] self.mixed_fields = [] @@ -773,12 +770,12 @@ class PreviewPanel(QWidget): """ Replacement for tag_callback. """ - try: + if self.is_connected: self.tags_updated.disconnect() - except RuntimeError: - pass + logging.info("[UPDATE CONTAINER] Setting tags updated slot") self.tags_updated.connect(slot) + self.is_connected = True # def write_container(self, item:Union[Entry, Collation, Tag], index, field): def write_container(self, index, field, mixed=False): @@ -1067,7 +1064,8 @@ class PreviewPanel(QWidget): ) # remove_mb.setStandardButtons(QMessageBox.StandardButton.Cancel) remove_mb.setDefaultButton(cancel_button) + remove_mb.setEscapeButton(cancel_button) result = remove_mb.exec_() # logging.info(result) - if result == 1: + if result == 3: callback() diff --git a/tagstudio/src/qt/widgets/thumb_button.py b/tagstudio/src/qt/widgets/thumb_button.py index 7878259..179efae 100644 --- a/tagstudio/src/qt/widgets/thumb_button.py +++ b/tagstudio/src/qt/widgets/thumb_button.py @@ -6,10 +6,11 @@ from PySide6 import QtCore from PySide6.QtCore import QEvent from PySide6.QtGui import QEnterEvent, QPainter, QColor, QPen, QPainterPath, QPaintEvent -from PySide6.QtWidgets import QWidget, QPushButton +from PySide6.QtWidgets import QWidget +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper -class ThumbButton(QPushButton): +class ThumbButton(QPushButtonWrapper): def __init__(self, parent: QWidget, thumb_size: tuple[int, int]) -> None: super().__init__(parent) self.thumb_size: tuple[int, int] = thumb_size