File: test_cmap.py

package info (click to toggle)
superqt 0.7.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,320 kB
  • sloc: python: 9,108; makefile: 16; sh: 12
file content (180 lines) | stat: -rw-r--r-- 5,888 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import platform
from unittest.mock import patch

import numpy as np
import pytest
from qtpy import API_NAME

try:
    from cmap import Colormap
except ImportError:
    pytest.skip("cmap not installed", allow_module_level=True)

from qtpy.QtCore import QRect
from qtpy.QtGui import QPainter, QPixmap
from qtpy.QtWidgets import QStyleOptionViewItem, QWidget

from superqt import QColormapComboBox
from superqt.cmap import (
    CmapCatalogComboBox,
    QColormapItemDelegate,
    QColormapLineEdit,
    _cmap_combo,
    draw_colormap,
)
from superqt.utils import qimage_to_array


def test_draw_cmap(qtbot):
    # draw into a QWidget
    wdg = QWidget()
    qtbot.addWidget(wdg)
    draw_colormap(wdg, "viridis")
    # draw into any QPaintDevice
    draw_colormap(QPixmap(), "viridis")
    # pass a painter an explicit colormap and a rect
    draw_colormap(QPainter(), Colormap(("red", "yellow", "blue")), QRect())
    # test with a border
    draw_colormap(wdg, "viridis", border_color="red", border_width=2)

    with pytest.raises(TypeError, match="Expected a QPainter or QPaintDevice instance"):
        draw_colormap(QRect(), "viridis")  # type: ignore

    with pytest.raises(TypeError, match="Expected a Colormap instance or something"):
        draw_colormap(QPainter(), "not a recognized string or cmap", QRect())


def test_cmap_draw_result():
    """Test that the image drawn actually looks correct."""
    # draw into any QPaintDevice
    w = 100
    h = 20
    pix = QPixmap(w, h)
    cmap = Colormap("viridis")
    draw_colormap(pix, cmap)

    ary1 = cmap(np.tile(np.linspace(0, 1, w), (h, 1)), bytes=True)
    ary2 = qimage_to_array(pix.toImage())

    # there are some subtle differences between how qimage draws and how
    # cmap draws, so we can't assert that the arrays are exactly equal.
    # they are visually indistinguishable, and numbers are close within 4 (/255) values
    # and linux, for some reason, is a bit more different``
    atol = 8 if platform.system() == "Linux" else 4
    np.testing.assert_allclose(ary1, ary2, atol=atol)

    cmap2 = Colormap(("#230777",), name="MyMap")
    draw_colormap(pix, cmap2)  # include transparency


def test_catalog_combo(qtbot):
    wdg = CmapCatalogComboBox()
    qtbot.addWidget(wdg)
    wdg.show()

    wdg.setCurrentText("viridis")
    assert wdg.currentColormap() == Colormap("viridis")


@pytest.mark.parametrize("filterable", [False, True])
def test_cmap_combo(qtbot, filterable):
    wdg = QColormapComboBox(allow_user_colormaps=True, filterable=filterable)
    qtbot.addWidget(wdg)
    wdg.show()
    assert wdg.userAdditionsAllowed()

    with qtbot.waitSignal(wdg.currentColormapChanged):
        wdg.addColormaps([Colormap("viridis"), "magma", ("red", "blue", "green")])
    assert wdg.currentColormap().name.split(":")[-1] == "viridis"

    with pytest.raises(ValueError, match="Invalid colormap"):
        wdg.addColormap("not a recognized string or cmap")

    assert wdg.currentColormap().name.split(":")[-1] == "viridis"
    assert wdg.currentIndex() == 0
    assert wdg.count() == 4  # includes "Add Colormap..."
    wdg.setCurrentColormap("magma")
    assert wdg.count() == 4  # make sure we didn't duplicate
    assert wdg.currentIndex() == 1

    if API_NAME == "PySide2":
        return  # the rest fails on CI... but works locally

    # click the Add Colormap... item
    # NOTE: We wrap __init__ instead of patching exec directly because
    # PySide6 6.10 crashes when MetaObjectBuilder inspects mocked methods
    # during signal connection (parsePythonType segfault)
    _original_init = _cmap_combo._CmapNameDialog.__init__

    def _init_with_mock_exec(self, *args, **kwargs):
        _original_init(self, *args, **kwargs)
        self.exec = lambda: True

    with qtbot.waitSignal(wdg.currentColormapChanged):
        with patch.object(
            _cmap_combo._CmapNameDialog, "__init__", _init_with_mock_exec
        ):
            wdg._on_activated(wdg.count() - 1)

    assert wdg.count() == 5
    # this could potentially fail in the future if cmap catalog changes
    # but mocking the return value of the dialog is also annoying
    assert wdg.itemColormap(3).name.split(":")[-1] == "accent"

    # click the Add Colormap... item, but cancel the dialog
    def _init_with_mock_exec_false(self, *args, **kwargs):
        _original_init(self, *args, **kwargs)
        self.exec = lambda: False

    with patch.object(
        _cmap_combo._CmapNameDialog, "__init__", _init_with_mock_exec_false
    ):
        wdg._on_activated(wdg.count() - 1)


def test_cmap_item_delegate(qtbot):
    wdg = CmapCatalogComboBox()
    qtbot.addWidget(wdg)
    view = wdg.view()
    delegate = view.itemDelegate()
    assert isinstance(delegate, QColormapItemDelegate)

    # smoke tests:
    painter = QPainter()
    option = QStyleOptionViewItem()
    index = wdg.model().index(0, 0)
    delegate._colormap_fraction = 1
    delegate.paint(painter, option, index)
    delegate._colormap_fraction = 0.33
    delegate.paint(painter, option, index)

    assert delegate.sizeHint(option, index) == delegate._item_size


def test_cmap_line_edit(qtbot, qapp):
    wdg = QColormapLineEdit()
    qtbot.addWidget(wdg)
    wdg.show()

    wdg.setColormap("viridis")
    assert wdg.colormap() == Colormap("viridis")
    wdg.setText("magma")  # also works if the name is recognized
    assert wdg.colormap() == Colormap("magma")
    qapp.processEvents()
    qtbot.wait(10)  # force the paintEvent

    wdg.setFractionalColormapWidth(1)
    assert wdg.fractionalColormapWidth() == 1
    wdg.update()
    qapp.processEvents()
    qtbot.wait(10)  # force the paintEvent

    wdg.setText("not-a-cmap")
    assert wdg.colormap() is None
    # or

    wdg.setFractionalColormapWidth(0.3)
    wdg.setColormap(None)
    assert wdg.colormap() is None
    qapp.processEvents()
    qtbot.wait(10)  # force the paintEvent