From 3d41a4fbc624ba89d46264daf0e1650b309cbc10 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Thu, 17 Nov 2022 09:09:55 -0600 Subject: [PATCH 01/19] add ability to change icon --- src/superqt/collapsible/_collapsible.py | 50 ++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 953ace25..89f3c3cb 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -9,7 +9,9 @@ QPropertyAnimation, Qt, Signal, + QRect, ) +from qtpy.QtGui import QIcon, QPainter, QPalette, QPixmap from qtpy.QtWidgets import QFrame, QPushButton, QVBoxLayout, QWidget @@ -30,9 +32,13 @@ def __init__(self, title: str = "", parent: Optional[QWidget] = None): super().__init__(parent) self._locked = False self._is_animating = False + self._text = title - self._toggle_btn = QPushButton(self._COLLAPSED + title) + self._toggle_btn = QPushButton(title) self._toggle_btn.setCheckable(True) + self.set_collapsed_icon() + self.set_expanded_icon() + self._toggle_btn.setIcon(self._COLLAPSED) self._toggle_btn.setStyleSheet("text-align: left; border: none; outline: none;") self._toggle_btn.toggled.connect(self._toggle) @@ -75,6 +81,46 @@ def content(self) -> QWidget: """Return the current content widget.""" return self._content + def set_expanded_icon(self, icon=None): + """Set the icon on the toggle button when the widget is expanded.""" + + if icon: + self._EXPANDED = icon + else: + pixmap = QPixmap(16, 16) + pixmap.fill(Qt.transparent) + painter = QPainter(pixmap) + color = self._toggle_btn.palette().color(QPalette.WindowText) + painter.setPen(color) + symbol = "▼" + painter.drawText(QRect(0, 0, 16, 16), Qt.AlignCenter, symbol) + painter.end() + + self._EXPANDED = QIcon(pixmap) + + del painter + del pixmap + + def set_collapsed_icon(self, icon=None): + """Set the icon on the toggle button when the widget is collapsed.""" + + if icon: + self._COLLAPSED = icon + else: + pixmap = QPixmap(16, 16) + pixmap.fill(Qt.transparent) + painter = QPainter(pixmap) + color = self._toggle_btn.palette().color(QPalette.WindowText) + painter.setPen(color) + symbol = "▲" + painter.drawText(QRect(0, 0, 16, 16), Qt.AlignCenter, symbol) + painter.end() + + self._COLLAPSED = QIcon(pixmap) + + del painter + del pixmap + def setDuration(self, msecs: int): """Set duration of the collapse/expand animation.""" self._animation.setDuration(msecs) @@ -122,9 +168,9 @@ def _expand_collapse( forward = direction == QPropertyAnimation.Direction.Forward text = self._EXPANDED if forward else self._COLLAPSED + self._toggle_btn.setIcon(text) self._toggle_btn.setChecked(forward) - self._toggle_btn.setText(text + self._toggle_btn.text()[len(self._EXPANDED) :]) _content_height = self._content.sizeHint().height() + 10 if animate: From 680fef6a4ed34792c3aa37fd5d89d170d2eec5a3 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Thu, 17 Nov 2022 09:57:32 -0600 Subject: [PATCH 02/19] fix icon setting so it will load properly on start up --- src/superqt/collapsible/_collapsible.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 89f3c3cb..9b8f1b2f 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -38,7 +38,6 @@ def __init__(self, title: str = "", parent: Optional[QWidget] = None): self._toggle_btn.setCheckable(True) self.set_collapsed_icon() self.set_expanded_icon() - self._toggle_btn.setIcon(self._COLLAPSED) self._toggle_btn.setStyleSheet("text-align: left; border: none; outline: none;") self._toggle_btn.toggled.connect(self._toggle) @@ -121,6 +120,8 @@ def set_collapsed_icon(self, icon=None): del painter del pixmap + self._toggle_btn.setIcon(self._COLLAPSED) + def setDuration(self, msecs: int): """Set duration of the collapse/expand animation.""" self._animation.setDuration(msecs) From c6e5617159834b7a613e604e7d7f7ed8cd1ad36d Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Thu, 17 Nov 2022 10:21:05 -0600 Subject: [PATCH 03/19] remove check on icon length. not necessary anymore --- src/superqt/collapsible/_collapsible.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 9b8f1b2f..3e7241cb 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -63,12 +63,12 @@ def __init__(self, title: str = "", parent: Optional[QWidget] = None): def setText(self, text: str): """Set the text of the toggle button.""" - current = self._toggle_btn.text()[: len(self._EXPANDED)] + current = self._toggle_btn.text() self._toggle_btn.setText(current + text) def text(self) -> str: """Return the text of the toggle button.""" - return self._toggle_btn.text()[len(self._EXPANDED) :] + return self._toggle_btn.text() def setContent(self, content: QWidget): """Replace central widget (the widget that gets expanded/collapsed).""" From 7455fe248b4999d6aae843dd0925e170590bbd5d Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Thu, 17 Nov 2022 10:26:04 -0600 Subject: [PATCH 04/19] fix test --- tests/test_collapsible.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index b73e8d70..f495c1bd 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -84,8 +84,7 @@ def test_changing_text(qtbot): wdg = QCollapsible() wdg.setText("Hi new text") assert wdg.text() == "Hi new text" - assert wdg._toggle_btn.text() == QCollapsible._COLLAPSED + "Hi new text" - + assert wdg._toggle_btn.text() == "Hi new text" def test_toggle_signal(qtbot): """Test that signal is emitted when widget expanded/collapsed.""" @@ -98,3 +97,4 @@ def test_toggle_signal(qtbot): with qtbot.waitSignal(wdg.toggled, timeout=500): wdg.collapse() + From 734ec08aedf9555640996191070890e09e307d77 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Fri, 18 Nov 2022 18:31:55 -0600 Subject: [PATCH 05/19] reduce duplicate code. expose _COLLAPSED and _EXPANDED to user on creation of QCollapsible widget --- src/superqt/collapsible/_collapsible.py | 56 +++++++++++-------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 3e7241cb..47980a4b 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -28,7 +28,13 @@ class QCollapsible(QFrame): toggled = Signal(bool) - def __init__(self, title: str = "", parent: Optional[QWidget] = None): + def __init__( + self, + title: str = "", + parent: Optional[QWidget] = None, + expandedIcon: Optional[QIcon] = None, + collapsedIcon: Optional[QIcon] = None, + ): super().__init__(parent) self._locked = False self._is_animating = False @@ -36,8 +42,8 @@ def __init__(self, title: str = "", parent: Optional[QWidget] = None): self._toggle_btn = QPushButton(title) self._toggle_btn.setCheckable(True) - self.set_collapsed_icon() - self.set_expanded_icon() + self.setCollapsedIcon(icon=collapsedIcon) + self.setExpandedIcon(icon=expandedIcon) self._toggle_btn.setStyleSheet("text-align: left; border: none; outline: none;") self._toggle_btn.toggled.connect(self._toggle) @@ -80,45 +86,33 @@ def content(self) -> QWidget: """Return the current content widget.""" return self._content - def set_expanded_icon(self, icon=None): + def _convert_symbol_to_icon(self, symbol: str) -> QIcon: + pixmap = QPixmap(16, 16) + pixmap.fill(Qt.transparent) + painter = QPainter(pixmap) + color = self._toggle_btn.palette().color(QPalette.WindowText) + painter.setPen(color) + painter.drawText(QRect(0, 0, 16, 16), Qt.AlignCenter, symbol) + painter.end() + return QIcon(pixmap) + + def setExpandedIcon(self, icon=None): """Set the icon on the toggle button when the widget is expanded.""" - if icon: + if icon and isinstance(icon, QIcon): self._EXPANDED = icon else: - pixmap = QPixmap(16, 16) - pixmap.fill(Qt.transparent) - painter = QPainter(pixmap) - color = self._toggle_btn.palette().color(QPalette.WindowText) - painter.setPen(color) symbol = "▼" - painter.drawText(QRect(0, 0, 16, 16), Qt.AlignCenter, symbol) - painter.end() - - self._EXPANDED = QIcon(pixmap) - - del painter - del pixmap + self._EXPANDED = self._convert_symbol_to_icon(symbol) - def set_collapsed_icon(self, icon=None): + def setCollapsedIcon(self, icon=None): """Set the icon on the toggle button when the widget is collapsed.""" - if icon: + if icon and isinstance(icon, QIcon): self._COLLAPSED = icon else: - pixmap = QPixmap(16, 16) - pixmap.fill(Qt.transparent) - painter = QPainter(pixmap) - color = self._toggle_btn.palette().color(QPalette.WindowText) - painter.setPen(color) symbol = "▲" - painter.drawText(QRect(0, 0, 16, 16), Qt.AlignCenter, symbol) - painter.end() - - self._COLLAPSED = QIcon(pixmap) - - del painter - del pixmap + self._COLLAPSED = self._convert_symbol_to_icon(symbol) self._toggle_btn.setIcon(self._COLLAPSED) From 7d5a9c64b59733d6ca57f74c5a66fd9c3912f69d Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Mon, 21 Nov 2022 16:02:08 -0600 Subject: [PATCH 06/19] add ability to set icon with string or icon. --- src/superqt/collapsible/_collapsible.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 47980a4b..a67a3665 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -1,5 +1,5 @@ """A collapsible widget to hide and unhide child widgets""" -from typing import Optional +from typing import Optional, Union from qtpy.QtCore import ( QEasingCurve, @@ -32,8 +32,8 @@ def __init__( self, title: str = "", parent: Optional[QWidget] = None, - expandedIcon: Optional[QIcon] = None, - collapsedIcon: Optional[QIcon] = None, + expandedIcon: Optional[Union[QIcon, str]] = None, + collapsedIcon: Optional[Union[QIcon, str]] = None, ): super().__init__(parent) self._locked = False @@ -96,20 +96,24 @@ def _convert_symbol_to_icon(self, symbol: str) -> QIcon: painter.end() return QIcon(pixmap) - def setExpandedIcon(self, icon=None): + def setExpandedIcon(self, icon: Optional[Union[QIcon, str]] = None): """Set the icon on the toggle button when the widget is expanded.""" if icon and isinstance(icon, QIcon): self._EXPANDED = icon + elif icon and isinstance(icon, str): + self._EXPANDED = self._convert_symbol_to_icon(icon) else: symbol = "▼" self._EXPANDED = self._convert_symbol_to_icon(symbol) - def setCollapsedIcon(self, icon=None): + def setCollapsedIcon(self, icon: Optional[Union[QIcon, str]] = None): """Set the icon on the toggle button when the widget is collapsed.""" if icon and isinstance(icon, QIcon): self._COLLAPSED = icon + elif icon and isinstance(icon, str): + self._COLLAPSED = self._convert_symbol_to_icon(icon) else: symbol = "▲" self._COLLAPSED = self._convert_symbol_to_icon(symbol) From 3e24fa118e120052f1a36080b7a7008e54573336 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Mon, 21 Nov 2022 16:02:47 -0600 Subject: [PATCH 07/19] add tests for adding, setting icons --- tests/test_collapsible.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index f495c1bd..a2ecd9f0 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -1,6 +1,8 @@ """A test module for testing collapsible""" -from qtpy.QtCore import QEasingCurve, Qt +import pytest +from napari._qt.qt_resources import QColoredSVGIcon +from qtpy.QtCore import QEasingCurve, Qt from qtpy.QtWidgets import QPushButton from superqt import QCollapsible @@ -98,3 +100,20 @@ def test_toggle_signal(qtbot): with qtbot.waitSignal(wdg.toggled, timeout=500): wdg.collapse() + +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_setting_icon(qtbot): + """Test setting icon for toggle button.""" + + icon1 = QColoredSVGIcon.from_resources("right_arrow") + icon2 = QColoredSVGIcon.from_resources("right_arrow") + + wdg = QCollapsible("test", expandedIcon=icon2, collapsedIcon=icon1) + assert wdg._EXPANDED == icon2 + assert wdg._COLLAPSED == icon1 + + icon2 = wdg._convert_symbol_to_icon("*") + wdg.setCollapsedIcon(icon=icon2) + assert wdg._COLLAPSED == icon2 + wdg.setExpandedIcon(icon=icon2) + assert wdg._EXPANDED == icon2 From 0e68d70dd00c818d893a07b0230698a5c3940cb7 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Mon, 21 Nov 2022 16:21:53 -0600 Subject: [PATCH 08/19] fix test. --- tests/test_collapsible.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index a2ecd9f0..ecae221b 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -1,11 +1,12 @@ """A test module for testing collapsible""" import pytest -from napari._qt.qt_resources import QColoredSVGIcon from qtpy.QtCore import QEasingCurve, Qt +from fonticon_fa5 import FA5S from qtpy.QtWidgets import QPushButton from superqt import QCollapsible +from superqt.fonticon import icon def test_checked_initialization(qtbot): @@ -105,12 +106,12 @@ def test_toggle_signal(qtbot): def test_setting_icon(qtbot): """Test setting icon for toggle button.""" - icon1 = QColoredSVGIcon.from_resources("right_arrow") - icon2 = QColoredSVGIcon.from_resources("right_arrow") + icon1 = icon(FA5S.smile, color="white") + icon2 = icon(FA5S.frown, color="white") - wdg = QCollapsible("test", expandedIcon=icon2, collapsedIcon=icon1) - assert wdg._EXPANDED == icon2 - assert wdg._COLLAPSED == icon1 + wdg = QCollapsible("test", expandedIcon=icon1, collapsedIcon=icon2) + assert wdg._EXPANDED == icon1 + assert wdg._COLLAPSED == icon2 icon2 = wdg._convert_symbol_to_icon("*") wdg.setCollapsedIcon(icon=icon2) From 80e57d4bdc1c33707de6fcc7bbc4d8204701df6d Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Wed, 23 Nov 2022 02:49:04 -0600 Subject: [PATCH 09/19] fix test for icons --- tests/test_collapsible.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index ecae221b..0086a7f2 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -1,12 +1,32 @@ """A test module for testing collapsible""" +from pathlib import Path import pytest from qtpy.QtCore import QEasingCurve, Qt -from fonticon_fa5 import FA5S from qtpy.QtWidgets import QPushButton from superqt import QCollapsible from superqt.fonticon import icon +from superqt.fonticon._qfont_icon import QFontIconStore + +TEST_PREFIX = "ico" +TEST_CHARNAME = "smiley" +TEST_CHAR = "\ue900" +TEST_GLYPHKEY = f"{TEST_PREFIX}.{TEST_CHARNAME}" +FONT_FILE = Path(__file__).parent / "test_fonticon/icontest.ttf" + + +@pytest.fixture +def store(qapp): + store = QFontIconStore().instance() + yield store + store.clear() + + +@pytest.fixture +def full_store(store): + store.addFont(str(FONT_FILE), TEST_PREFIX, {TEST_CHARNAME: TEST_CHAR}) + return store def test_checked_initialization(qtbot): @@ -103,11 +123,10 @@ def test_toggle_signal(qtbot): @pytest.mark.filterwarnings("ignore::DeprecationWarning") -def test_setting_icon(qtbot): +def test_setting_icon(qtbot, full_store): """Test setting icon for toggle button.""" - - icon1 = icon(FA5S.smile, color="white") - icon2 = icon(FA5S.frown, color="white") + icon1 = icon(TEST_GLYPHKEY) + icon2 = icon(TEST_GLYPHKEY) wdg = QCollapsible("test", expandedIcon=icon1, collapsedIcon=icon2) assert wdg._EXPANDED == icon1 From 9a7fb6f72125db4660ea2fab50dabf2958b3621c Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Wed, 23 Nov 2022 02:53:40 -0600 Subject: [PATCH 10/19] move file --- tests/{ => test_fonticon}/test_collapsible.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{ => test_fonticon}/test_collapsible.py (98%) diff --git a/tests/test_collapsible.py b/tests/test_fonticon/test_collapsible.py similarity index 98% rename from tests/test_collapsible.py rename to tests/test_fonticon/test_collapsible.py index 0086a7f2..a19d3ade 100644 --- a/tests/test_collapsible.py +++ b/tests/test_fonticon/test_collapsible.py @@ -13,7 +13,7 @@ TEST_CHARNAME = "smiley" TEST_CHAR = "\ue900" TEST_GLYPHKEY = f"{TEST_PREFIX}.{TEST_CHARNAME}" -FONT_FILE = Path(__file__).parent / "test_fonticon/icontest.ttf" +FONT_FILE = Path(__file__).parent / "icontest.ttf" @pytest.fixture From 309d5f19af02f6bfb8ac9cfecd18e0ba93ea982b Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Sat, 26 Nov 2022 02:33:11 -0600 Subject: [PATCH 11/19] fix test --- tests/{test_fonticon => }/test_collapsible.py | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) rename tests/{test_fonticon => }/test_collapsible.py (79%) diff --git a/tests/test_fonticon/test_collapsible.py b/tests/test_collapsible.py similarity index 79% rename from tests/test_fonticon/test_collapsible.py rename to tests/test_collapsible.py index a19d3ade..cf1b980e 100644 --- a/tests/test_fonticon/test_collapsible.py +++ b/tests/test_collapsible.py @@ -1,32 +1,23 @@ """A test module for testing collapsible""" -from pathlib import Path import pytest from qtpy.QtCore import QEasingCurve, Qt from qtpy.QtWidgets import QPushButton +from qtpy import PYQT6, PYSIDE6 +from qtpy.QtWidgets import QPushButton, QStyle, QWidget from superqt import QCollapsible -from superqt.fonticon import icon -from superqt.fonticon._qfont_icon import QFontIconStore -TEST_PREFIX = "ico" -TEST_CHARNAME = "smiley" -TEST_CHAR = "\ue900" -TEST_GLYPHKEY = f"{TEST_PREFIX}.{TEST_CHARNAME}" -FONT_FILE = Path(__file__).parent / "icontest.ttf" +def _get_builtin_icon(name): + """Get a built-in icon from the Qt library.""" + widget = QWidget() + if PYQT6 or PYSIDE6: + pixmap = getattr(QStyle.StandardPixmap, f"SP_{name}") + else: + pixmap = getattr(QStyle, f"SP_{name}") -@pytest.fixture -def store(qapp): - store = QFontIconStore().instance() - yield store - store.clear() - - -@pytest.fixture -def full_store(store): - store.addFont(str(FONT_FILE), TEST_PREFIX, {TEST_CHARNAME: TEST_CHAR}) - return store + return widget.style().standardIcon(pixmap) def test_checked_initialization(qtbot): @@ -123,17 +114,21 @@ def test_toggle_signal(qtbot): @pytest.mark.filterwarnings("ignore::DeprecationWarning") -def test_setting_icon(qtbot, full_store): +def test_setting_icon(qtbot): """Test setting icon for toggle button.""" - icon1 = icon(TEST_GLYPHKEY) - icon2 = icon(TEST_GLYPHKEY) - + icon1 = _get_builtin_icon("ArrowRight") + icon2 = _get_builtin_icon("ArrowDown") wdg = QCollapsible("test", expandedIcon=icon1, collapsedIcon=icon2) assert wdg._EXPANDED == icon1 assert wdg._COLLAPSED == icon2 - icon2 = wdg._convert_symbol_to_icon("*") - wdg.setCollapsedIcon(icon=icon2) - assert wdg._COLLAPSED == icon2 + +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_setting_symbol_icon(qtbot): + wdg = QCollapsible("test") + icon1 = wdg._convert_symbol_to_icon("+") + icon2 = wdg._convert_symbol_to_icon("-") + wdg.setCollapsedIcon(icon=icon1) + assert wdg._COLLAPSED == icon1 wdg.setExpandedIcon(icon=icon2) assert wdg._EXPANDED == icon2 From e7d25fa261be5a91091a770c4c08280e676397d0 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Sat, 26 Nov 2022 02:49:30 -0600 Subject: [PATCH 12/19] remove hardcoded size. Use font size --- src/superqt/collapsible/_collapsible.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index a67a3665..90664fa7 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -87,12 +87,13 @@ def content(self) -> QWidget: return self._content def _convert_symbol_to_icon(self, symbol: str) -> QIcon: - pixmap = QPixmap(16, 16) + size = self._toggle_btn.font().pointSize() + pixmap = QPixmap(size, size) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) color = self._toggle_btn.palette().color(QPalette.WindowText) painter.setPen(color) - painter.drawText(QRect(0, 0, 16, 16), Qt.AlignCenter, symbol) + painter.drawText(QRect(0, 0, size, size), Qt.AlignCenter, symbol) painter.end() return QIcon(pixmap) From 2f5a5eb3b977425d56286493c62d7c1845f850c6 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Sat, 26 Nov 2022 02:53:18 -0600 Subject: [PATCH 13/19] add test docstring --- tests/test_collapsible.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index cf1b980e..d368174e 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -125,6 +125,7 @@ def test_setting_icon(qtbot): @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_setting_symbol_icon(qtbot): + """Test setting string as toggle button.""" wdg = QCollapsible("test") icon1 = wdg._convert_symbol_to_icon("+") icon2 = wdg._convert_symbol_to_icon("-") From d451e58a7ed57065af60f93024b95e48a95dc45b Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Mon, 28 Nov 2022 15:57:26 -0600 Subject: [PATCH 14/19] fix test. chnage expanded/collapsed names --- src/superqt/collapsible/_collapsible.py | 22 +++++++++++----------- tests/test_collapsible.py | 19 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 90664fa7..64a891cd 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -7,9 +7,9 @@ QMargins, QObject, QPropertyAnimation, + QRect, Qt, Signal, - QRect, ) from qtpy.QtGui import QIcon, QPainter, QPalette, QPixmap from qtpy.QtWidgets import QFrame, QPushButton, QVBoxLayout, QWidget @@ -23,8 +23,8 @@ class QCollapsible(QFrame): Based on https://stackoverflow.com/a/68141638 """ - _EXPANDED = "▼ " - _COLLAPSED = "▲ " + _expanded_icon = "▼ " + _collapsed_icon = "▲ " toggled = Signal(bool) @@ -101,25 +101,25 @@ def setExpandedIcon(self, icon: Optional[Union[QIcon, str]] = None): """Set the icon on the toggle button when the widget is expanded.""" if icon and isinstance(icon, QIcon): - self._EXPANDED = icon + self._expanded_icon = icon elif icon and isinstance(icon, str): - self._EXPANDED = self._convert_symbol_to_icon(icon) + self._expanded_icon = self._convert_symbol_to_icon(icon) else: symbol = "▼" - self._EXPANDED = self._convert_symbol_to_icon(symbol) + self._expanded_icon = self._convert_symbol_to_icon(symbol) def setCollapsedIcon(self, icon: Optional[Union[QIcon, str]] = None): """Set the icon on the toggle button when the widget is collapsed.""" if icon and isinstance(icon, QIcon): - self._COLLAPSED = icon + self._collapsed_icon = icon elif icon and isinstance(icon, str): - self._COLLAPSED = self._convert_symbol_to_icon(icon) + self._collapsed_icon = self._convert_symbol_to_icon(icon) else: symbol = "▲" - self._COLLAPSED = self._convert_symbol_to_icon(symbol) + self._collapsed_icon = self._convert_symbol_to_icon(symbol) - self._toggle_btn.setIcon(self._COLLAPSED) + self._toggle_btn.setIcon(self._collapsed_icon) def setDuration(self, msecs: int): """Set duration of the collapse/expand animation.""" @@ -167,7 +167,7 @@ def _expand_collapse( return forward = direction == QPropertyAnimation.Direction.Forward - text = self._EXPANDED if forward else self._COLLAPSED + text = self._expanded_icon if forward else self._collapsed_icon self._toggle_btn.setIcon(text) self._toggle_btn.setChecked(forward) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index d368174e..7b0970a0 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -1,9 +1,7 @@ """A test module for testing collapsible""" import pytest -from qtpy.QtCore import QEasingCurve, Qt -from qtpy.QtWidgets import QPushButton -from qtpy import PYQT6, PYSIDE6 +from qtpy.QtCore import QEasingCurve, Qt from qtpy.QtWidgets import QPushButton, QStyle, QWidget from superqt import QCollapsible @@ -12,9 +10,9 @@ def _get_builtin_icon(name): """Get a built-in icon from the Qt library.""" widget = QWidget() - if PYQT6 or PYSIDE6: + try: pixmap = getattr(QStyle.StandardPixmap, f"SP_{name}") - else: + except AttributeError: pixmap = getattr(QStyle, f"SP_{name}") return widget.style().standardIcon(pixmap) @@ -100,6 +98,7 @@ def test_changing_text(qtbot): assert wdg.text() == "Hi new text" assert wdg._toggle_btn.text() == "Hi new text" + def test_toggle_signal(qtbot): """Test that signal is emitted when widget expanded/collapsed.""" wdg = QCollapsible() @@ -111,7 +110,7 @@ def test_toggle_signal(qtbot): with qtbot.waitSignal(wdg.toggled, timeout=500): wdg.collapse() - + @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_setting_icon(qtbot): @@ -119,8 +118,8 @@ def test_setting_icon(qtbot): icon1 = _get_builtin_icon("ArrowRight") icon2 = _get_builtin_icon("ArrowDown") wdg = QCollapsible("test", expandedIcon=icon1, collapsedIcon=icon2) - assert wdg._EXPANDED == icon1 - assert wdg._COLLAPSED == icon2 + assert wdg._expanded_icon == icon1 + assert wdg._collapsed_icon == icon2 @pytest.mark.filterwarnings("ignore::DeprecationWarning") @@ -130,6 +129,6 @@ def test_setting_symbol_icon(qtbot): icon1 = wdg._convert_symbol_to_icon("+") icon2 = wdg._convert_symbol_to_icon("-") wdg.setCollapsedIcon(icon=icon1) - assert wdg._COLLAPSED == icon1 + assert wdg._collapsed_icon == icon1 wdg.setExpandedIcon(icon=icon2) - assert wdg._EXPANDED == icon2 + assert wdg._expanded_icon == icon2 From 72d38489754d0d0228062904ced5f925a8125e53 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Tue, 29 Nov 2022 00:16:52 -0600 Subject: [PATCH 15/19] remove unnecessary strings --- src/superqt/collapsible/_collapsible.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 64a891cd..111dd960 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -23,9 +23,6 @@ class QCollapsible(QFrame): Based on https://stackoverflow.com/a/68141638 """ - _expanded_icon = "▼ " - _collapsed_icon = "▲ " - toggled = Signal(bool) def __init__( From 0936ade74232cf55e5f87f41701a5a2e9197a622 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Tue, 29 Nov 2022 13:11:16 -0600 Subject: [PATCH 16/19] update example. add getter functions. remove lines. change function name --- examples/qcollapsible.py | 2 ++ src/superqt/collapsible/_collapsible.py | 21 ++++++++++++++------- tests/test_collapsible.py | 4 ++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/qcollapsible.py b/examples/qcollapsible.py index a57c8853..8678e0f5 100644 --- a/examples/qcollapsible.py +++ b/examples/qcollapsible.py @@ -6,6 +6,8 @@ app = QApplication([]) collapsible = QCollapsible("Advanced analysis") +collapsible.setCollapsedIcon("+") +collapsible.setExpandedIcon("-") collapsible.addWidget(QLabel("This is the inside of the collapsible frame")) for i in range(10): collapsible.addWidget(QPushButton(f"Content button {i + 1}")) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 111dd960..2acd2acf 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -83,7 +83,8 @@ def content(self) -> QWidget: """Return the current content widget.""" return self._content - def _convert_symbol_to_icon(self, symbol: str) -> QIcon: + def _convert_string_to_icon(self, symbol: str) -> QIcon: + """Create a QIcon from a string.""" size = self._toggle_btn.font().pointSize() pixmap = QPixmap(size, size) pixmap.fill(Qt.transparent) @@ -96,28 +97,34 @@ def _convert_symbol_to_icon(self, symbol: str) -> QIcon: def setExpandedIcon(self, icon: Optional[Union[QIcon, str]] = None): """Set the icon on the toggle button when the widget is expanded.""" - if icon and isinstance(icon, QIcon): self._expanded_icon = icon elif icon and isinstance(icon, str): - self._expanded_icon = self._convert_symbol_to_icon(icon) + self._expanded_icon = self._convert_string_to_icon(icon) else: symbol = "▼" - self._expanded_icon = self._convert_symbol_to_icon(symbol) + self._expanded_icon = self._convert_string_to_icon(symbol) + + def expandedIcon(self): + """Returns the icon used when the widget is expanded.""" + return self._expanded_icon def setCollapsedIcon(self, icon: Optional[Union[QIcon, str]] = None): """Set the icon on the toggle button when the widget is collapsed.""" - if icon and isinstance(icon, QIcon): self._collapsed_icon = icon elif icon and isinstance(icon, str): - self._collapsed_icon = self._convert_symbol_to_icon(icon) + self._collapsed_icon = self._convert_string_to_icon(icon) else: symbol = "▲" - self._collapsed_icon = self._convert_symbol_to_icon(symbol) + self._collapsed_icon = self._convert_string_to_icon(symbol) self._toggle_btn.setIcon(self._collapsed_icon) + def collapsedIcon(self): + """Returns the icon used when the widget is collapsed.""" + return self._collapsed_icon + def setDuration(self, msecs: int): """Set duration of the collapse/expand animation.""" self._animation.setDuration(msecs) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index 7b0970a0..d2a04e8d 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -126,8 +126,8 @@ def test_setting_icon(qtbot): def test_setting_symbol_icon(qtbot): """Test setting string as toggle button.""" wdg = QCollapsible("test") - icon1 = wdg._convert_symbol_to_icon("+") - icon2 = wdg._convert_symbol_to_icon("-") + icon1 = wdg._convert_string_to_icon("+") + icon2 = wdg._convert_string_to_icon("-") wdg.setCollapsedIcon(icon=icon1) assert wdg._collapsed_icon == icon1 wdg.setExpandedIcon(icon=icon2) From 25fe6f1c5b12fd479cd100f41e495b3033e74429 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Tue, 29 Nov 2022 13:27:40 -0600 Subject: [PATCH 17/19] put default string in init. add getter tests --- src/superqt/collapsible/_collapsible.py | 10 ++-------- tests/test_collapsible.py | 8 ++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index 2acd2acf..d64fb428 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -29,8 +29,8 @@ def __init__( self, title: str = "", parent: Optional[QWidget] = None, - expandedIcon: Optional[Union[QIcon, str]] = None, - collapsedIcon: Optional[Union[QIcon, str]] = None, + expandedIcon: Optional[Union[QIcon, str]] = "▼", + collapsedIcon: Optional[Union[QIcon, str]] = "▲", ): super().__init__(parent) self._locked = False @@ -101,9 +101,6 @@ def setExpandedIcon(self, icon: Optional[Union[QIcon, str]] = None): self._expanded_icon = icon elif icon and isinstance(icon, str): self._expanded_icon = self._convert_string_to_icon(icon) - else: - symbol = "▼" - self._expanded_icon = self._convert_string_to_icon(symbol) def expandedIcon(self): """Returns the icon used when the widget is expanded.""" @@ -115,9 +112,6 @@ def setCollapsedIcon(self, icon: Optional[Union[QIcon, str]] = None): self._collapsed_icon = icon elif icon and isinstance(icon, str): self._collapsed_icon = self._convert_string_to_icon(icon) - else: - symbol = "▲" - self._collapsed_icon = self._convert_string_to_icon(symbol) self._toggle_btn.setIcon(self._collapsed_icon) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index d2a04e8d..406b5135 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -112,6 +112,14 @@ def test_toggle_signal(qtbot): wdg.collapse() +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_getting_icon(qtbot): + """Test setting string as toggle button.""" + wdg = QCollapsible("test") + wdg.expandedIcon() + wdg.collapsedIcon() + + @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_setting_icon(qtbot): """Test setting icon for toggle button.""" From 8a278489b9104d924654482bdf67e36f2c202fe4 Mon Sep 17 00:00:00 2001 From: Pam Wadhwa Date: Tue, 29 Nov 2022 13:31:17 -0600 Subject: [PATCH 18/19] update test --- tests/test_collapsible.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index 406b5135..437f6ff1 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -2,6 +2,7 @@ import pytest from qtpy.QtCore import QEasingCurve, Qt +from qtpy.QtGui import QIcon from qtpy.QtWidgets import QPushButton, QStyle, QWidget from superqt import QCollapsible @@ -116,8 +117,8 @@ def test_toggle_signal(qtbot): def test_getting_icon(qtbot): """Test setting string as toggle button.""" wdg = QCollapsible("test") - wdg.expandedIcon() - wdg.collapsedIcon() + assert isinstance(wdg.expandedIcon(), QIcon) + assert isinstance(wdg.collapsedIcon(), QIcon) @pytest.mark.filterwarnings("ignore::DeprecationWarning") From 8a4b7fc81aafb044a4e75b0e630bf54cbc5c9eac Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 30 Nov 2022 20:36:33 -0500 Subject: [PATCH 19/19] cleanup typing and fix set setCollapsedIcon --- src/superqt/collapsible/_collapsible.py | 59 +++++++++++++------------ tests/test_collapsible.py | 6 +-- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/superqt/collapsible/_collapsible.py b/src/superqt/collapsible/_collapsible.py index d64fb428..e2b42769 100644 --- a/src/superqt/collapsible/_collapsible.py +++ b/src/superqt/collapsible/_collapsible.py @@ -64,7 +64,7 @@ def __init__( _content.layout().setContentsMargins(QMargins(5, 0, 0, 0)) self.setContent(_content) - def setText(self, text: str): + def setText(self, text: str) -> None: """Set the text of the toggle button.""" current = self._toggle_btn.text() self._toggle_btn.setText(current + text) @@ -73,7 +73,7 @@ def text(self) -> str: """Return the text of the toggle button.""" return self._toggle_btn.text() - def setContent(self, content: QWidget): + def setContent(self, content: QWidget) -> None: """Replace central widget (the widget that gets expanded/collapsed).""" self._content = content self.layout().addWidget(self._content) @@ -87,61 +87,65 @@ def _convert_string_to_icon(self, symbol: str) -> QIcon: """Create a QIcon from a string.""" size = self._toggle_btn.font().pointSize() pixmap = QPixmap(size, size) - pixmap.fill(Qt.transparent) + pixmap.fill(Qt.GlobalColor.transparent) painter = QPainter(pixmap) - color = self._toggle_btn.palette().color(QPalette.WindowText) + color = self._toggle_btn.palette().color(QPalette.ColorRole.WindowText) painter.setPen(color) - painter.drawText(QRect(0, 0, size, size), Qt.AlignCenter, symbol) + painter.drawText(QRect(0, 0, size, size), Qt.AlignmentFlag.AlignCenter, symbol) painter.end() return QIcon(pixmap) - def setExpandedIcon(self, icon: Optional[Union[QIcon, str]] = None): + def expandedIcon(self) -> QIcon: + """Returns the icon used when the widget is expanded.""" + return self._expanded_icon + + def setExpandedIcon(self, icon: Optional[Union[QIcon, str]] = None) -> None: """Set the icon on the toggle button when the widget is expanded.""" if icon and isinstance(icon, QIcon): self._expanded_icon = icon elif icon and isinstance(icon, str): self._expanded_icon = self._convert_string_to_icon(icon) - def expandedIcon(self): - """Returns the icon used when the widget is expanded.""" - return self._expanded_icon + if self.isExpanded(): + self._toggle_btn.setIcon(self._expanded_icon) - def setCollapsedIcon(self, icon: Optional[Union[QIcon, str]] = None): + def collapsedIcon(self) -> QIcon: + """Returns the icon used when the widget is collapsed.""" + return self._collapsed_icon + + def setCollapsedIcon(self, icon: Optional[Union[QIcon, str]] = None) -> None: """Set the icon on the toggle button when the widget is collapsed.""" if icon and isinstance(icon, QIcon): self._collapsed_icon = icon elif icon and isinstance(icon, str): self._collapsed_icon = self._convert_string_to_icon(icon) - self._toggle_btn.setIcon(self._collapsed_icon) - - def collapsedIcon(self): - """Returns the icon used when the widget is collapsed.""" - return self._collapsed_icon + if not self.isExpanded(): + self._toggle_btn.setIcon(self._collapsed_icon) - def setDuration(self, msecs: int): + def setDuration(self, msecs: int) -> None: """Set duration of the collapse/expand animation.""" self._animation.setDuration(msecs) - def setEasingCurve(self, easing: QEasingCurve): + def setEasingCurve(self, easing: QEasingCurve) -> None: """Set the easing curve for the collapse/expand animation""" self._animation.setEasingCurve(easing) - def addWidget(self, widget: QWidget): + def addWidget(self, widget: QWidget) -> None: """Add a widget to the central content widget's layout.""" widget.installEventFilter(self) self._content.layout().addWidget(widget) - def removeWidget(self, widget: QWidget): + def removeWidget(self, widget: QWidget) -> None: """Remove widget from the central content widget's layout.""" self._content.layout().removeWidget(widget) widget.removeEventFilter(self) - def expand(self, animate: bool = True): + def expand(self, animate: bool = True) -> None: """Expand (show) the collapsible section""" self._expand_collapse(QPropertyAnimation.Direction.Forward, animate) - def collapse(self, animate: bool = True): + def collapse(self, animate: bool = True) -> None: """Collapse (hide) the collapsible section""" self._expand_collapse(QPropertyAnimation.Direction.Backward, animate) @@ -149,7 +153,7 @@ def isExpanded(self) -> bool: """Return whether the collapsible section is visible""" return self._toggle_btn.isChecked() - def setLocked(self, locked: bool = True): + def setLocked(self, locked: bool = True) -> None: """Set whether collapse/expand is disabled""" self._locked = locked self._toggle_btn.setCheckable(not locked) @@ -160,14 +164,13 @@ def locked(self) -> bool: def _expand_collapse( self, direction: QPropertyAnimation.Direction, animate: bool = True - ): + ) -> None: if self._locked: return forward = direction == QPropertyAnimation.Direction.Forward - text = self._expanded_icon if forward else self._collapsed_icon - self._toggle_btn.setIcon(text) - + icon = self._expanded_icon if forward else self._collapsed_icon + self._toggle_btn.setIcon(icon) self._toggle_btn.setChecked(forward) _content_height = self._content.sizeHint().height() + 10 @@ -181,7 +184,7 @@ def _expand_collapse( self.toggled.emit(direction == QPropertyAnimation.Direction.Forward) - def _toggle(self): + def _toggle(self) -> None: self.expand() if self.isExpanded() else self.collapse() def eventFilter(self, a0: QObject, a1: QEvent) -> bool: @@ -194,5 +197,5 @@ def eventFilter(self, a0: QObject, a1: QEvent) -> bool: self._expand_collapse(QPropertyAnimation.Direction.Forward, animate=False) return False - def _on_animation_done(self): + def _on_animation_done(self) -> None: self._is_animating = False diff --git a/tests/test_collapsible.py b/tests/test_collapsible.py index 437f6ff1..2661d6ed 100644 --- a/tests/test_collapsible.py +++ b/tests/test_collapsible.py @@ -1,6 +1,5 @@ """A test module for testing collapsible""" -import pytest from qtpy.QtCore import QEasingCurve, Qt from qtpy.QtGui import QIcon from qtpy.QtWidgets import QPushButton, QStyle, QWidget @@ -8,7 +7,7 @@ from superqt import QCollapsible -def _get_builtin_icon(name): +def _get_builtin_icon(name: str) -> QIcon: """Get a built-in icon from the Qt library.""" widget = QWidget() try: @@ -113,7 +112,6 @@ def test_toggle_signal(qtbot): wdg.collapse() -@pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_getting_icon(qtbot): """Test setting string as toggle button.""" wdg = QCollapsible("test") @@ -121,7 +119,6 @@ def test_getting_icon(qtbot): assert isinstance(wdg.collapsedIcon(), QIcon) -@pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_setting_icon(qtbot): """Test setting icon for toggle button.""" icon1 = _get_builtin_icon("ArrowRight") @@ -131,7 +128,6 @@ def test_setting_icon(qtbot): assert wdg._collapsed_icon == icon2 -@pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_setting_symbol_icon(qtbot): """Test setting string as toggle button.""" wdg = QCollapsible("test")