123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- from types import SimpleNamespace
- import matplotlib.widgets as widgets
- import matplotlib.pyplot as plt
- from matplotlib.testing.decorators import image_comparison
- from numpy.testing import assert_allclose
- import pytest
- def get_ax():
- fig, ax = plt.subplots(1, 1)
- ax.plot([0, 200], [0, 200])
- ax.set_aspect(1.0)
- ax.figure.canvas.draw()
- return ax
- def do_event(tool, etype, button=1, xdata=0, ydata=0, key=None, step=1):
- """
- *name*
- the event name
- *canvas*
- the FigureCanvas instance generating the event
- *guiEvent*
- the GUI event that triggered the matplotlib event
- *x*
- x position - pixels from left of canvas
- *y*
- y position - pixels from bottom of canvas
- *inaxes*
- the :class:`~matplotlib.axes.Axes` instance if mouse is over axes
- *xdata*
- x coord of mouse in data coords
- *ydata*
- y coord of mouse in data coords
- *button*
- button pressed None, 1, 2, 3, 'up', 'down' (up and down are used
- for scroll events)
- *key*
- the key depressed when the mouse event triggered (see
- :class:`KeyEvent`)
- *step*
- number of scroll steps (positive for 'up', negative for 'down')
- """
- event = SimpleNamespace()
- event.button = button
- ax = tool.ax
- event.x, event.y = ax.transData.transform([(xdata, ydata),
- (xdata, ydata)])[00]
- event.xdata, event.ydata = xdata, ydata
- event.inaxes = ax
- event.canvas = ax.figure.canvas
- event.key = key
- event.step = step
- event.guiEvent = None
- event.name = 'Custom'
- func = getattr(tool, etype)
- func(event)
- def check_rectangle(**kwargs):
- ax = get_ax()
- def onselect(epress, erelease):
- ax._got_onselect = True
- assert epress.xdata == 100
- assert epress.ydata == 100
- assert erelease.xdata == 199
- assert erelease.ydata == 199
- tool = widgets.RectangleSelector(ax, onselect, **kwargs)
- do_event(tool, 'press', xdata=100, ydata=100, button=1)
- do_event(tool, 'onmove', xdata=199, ydata=199, button=1)
- # purposely drag outside of axis for release
- do_event(tool, 'release', xdata=250, ydata=250, button=1)
- if kwargs.get('drawtype', None) not in ['line', 'none']:
- assert_allclose(tool.geometry,
- [[100., 100, 199, 199, 100],
- [100, 199, 199, 100, 100]],
- err_msg=tool.geometry)
- assert ax._got_onselect
- def test_rectangle_selector():
- check_rectangle()
- check_rectangle(drawtype='line', useblit=False)
- check_rectangle(useblit=True, button=1)
- check_rectangle(drawtype='none', minspanx=10, minspany=10)
- check_rectangle(minspanx=10, minspany=10, spancoords='pixels')
- check_rectangle(rectprops=dict(fill=True))
- def test_ellipse():
- """For ellipse, test out the key modifiers"""
- ax = get_ax()
- def onselect(epress, erelease):
- pass
- tool = widgets.EllipseSelector(ax, onselect=onselect,
- maxdist=10, interactive=True)
- tool.extents = (100, 150, 100, 150)
- # drag the rectangle
- do_event(tool, 'press', xdata=10, ydata=10, button=1,
- key=' ')
- do_event(tool, 'onmove', xdata=30, ydata=30, button=1)
- do_event(tool, 'release', xdata=30, ydata=30, button=1)
- assert tool.extents == (120, 170, 120, 170)
- # create from center
- do_event(tool, 'on_key_press', xdata=100, ydata=100, button=1,
- key='control')
- do_event(tool, 'press', xdata=100, ydata=100, button=1)
- do_event(tool, 'onmove', xdata=125, ydata=125, button=1)
- do_event(tool, 'release', xdata=125, ydata=125, button=1)
- do_event(tool, 'on_key_release', xdata=100, ydata=100, button=1,
- key='control')
- assert tool.extents == (75, 125, 75, 125)
- # create a square
- do_event(tool, 'on_key_press', xdata=10, ydata=10, button=1,
- key='shift')
- do_event(tool, 'press', xdata=10, ydata=10, button=1)
- do_event(tool, 'onmove', xdata=35, ydata=30, button=1)
- do_event(tool, 'release', xdata=35, ydata=30, button=1)
- do_event(tool, 'on_key_release', xdata=10, ydata=10, button=1,
- key='shift')
- extents = [int(e) for e in tool.extents]
- assert extents == [10, 35, 10, 34]
- # create a square from center
- do_event(tool, 'on_key_press', xdata=100, ydata=100, button=1,
- key='ctrl+shift')
- do_event(tool, 'press', xdata=100, ydata=100, button=1)
- do_event(tool, 'onmove', xdata=125, ydata=130, button=1)
- do_event(tool, 'release', xdata=125, ydata=130, button=1)
- do_event(tool, 'on_key_release', xdata=100, ydata=100, button=1,
- key='ctrl+shift')
- extents = [int(e) for e in tool.extents]
- assert extents == [70, 129, 70, 130]
- assert tool.geometry.shape == (2, 73)
- assert_allclose(tool.geometry[:, 0], [70., 100])
- def test_rectangle_handles():
- ax = get_ax()
- def onselect(epress, erelease):
- pass
- tool = widgets.RectangleSelector(ax, onselect=onselect,
- maxdist=10, interactive=True)
- tool.extents = (100, 150, 100, 150)
- assert tool.corners == (
- (100, 150, 150, 100), (100, 100, 150, 150))
- assert tool.extents == (100, 150, 100, 150)
- assert tool.edge_centers == (
- (100, 125.0, 150, 125.0), (125.0, 100, 125.0, 150))
- assert tool.extents == (100, 150, 100, 150)
- # grab a corner and move it
- do_event(tool, 'press', xdata=100, ydata=100)
- do_event(tool, 'onmove', xdata=120, ydata=120)
- do_event(tool, 'release', xdata=120, ydata=120)
- assert tool.extents == (120, 150, 120, 150)
- # grab the center and move it
- do_event(tool, 'press', xdata=132, ydata=132)
- do_event(tool, 'onmove', xdata=120, ydata=120)
- do_event(tool, 'release', xdata=120, ydata=120)
- assert tool.extents == (108, 138, 108, 138)
- # create a new rectangle
- do_event(tool, 'press', xdata=10, ydata=10)
- do_event(tool, 'onmove', xdata=100, ydata=100)
- do_event(tool, 'release', xdata=100, ydata=100)
- assert tool.extents == (10, 100, 10, 100)
- def check_span(*args, **kwargs):
- ax = get_ax()
- def onselect(vmin, vmax):
- ax._got_onselect = True
- assert vmin == 100
- assert vmax == 150
- def onmove(vmin, vmax):
- assert vmin == 100
- assert vmax == 125
- ax._got_on_move = True
- if 'onmove_callback' in kwargs:
- kwargs['onmove_callback'] = onmove
- tool = widgets.SpanSelector(ax, onselect, *args, **kwargs)
- do_event(tool, 'press', xdata=100, ydata=100, button=1)
- do_event(tool, 'onmove', xdata=125, ydata=125, button=1)
- do_event(tool, 'release', xdata=150, ydata=150, button=1)
- assert ax._got_onselect
- if 'onmove_callback' in kwargs:
- assert ax._got_on_move
- def test_span_selector():
- check_span('horizontal', minspan=10, useblit=True)
- check_span('vertical', onmove_callback=True, button=1)
- check_span('horizontal', rectprops=dict(fill=True))
- def check_lasso_selector(**kwargs):
- ax = get_ax()
- def onselect(verts):
- ax._got_onselect = True
- assert verts == [(100, 100), (125, 125), (150, 150)]
- tool = widgets.LassoSelector(ax, onselect, **kwargs)
- do_event(tool, 'press', xdata=100, ydata=100, button=1)
- do_event(tool, 'onmove', xdata=125, ydata=125, button=1)
- do_event(tool, 'release', xdata=150, ydata=150, button=1)
- assert ax._got_onselect
- def test_lasso_selector():
- check_lasso_selector()
- check_lasso_selector(useblit=False, lineprops=dict(color='red'))
- check_lasso_selector(useblit=True, button=1)
- def test_CheckButtons():
- ax = get_ax()
- check = widgets.CheckButtons(ax, ('a', 'b', 'c'), (True, False, True))
- assert check.get_status() == [True, False, True]
- check.set_active(0)
- assert check.get_status() == [False, False, True]
- cid = check.on_clicked(lambda: None)
- check.disconnect(cid)
- @image_comparison(['check_radio_buttons.png'], style='mpl20', remove_text=True)
- def test_check_radio_buttons_image():
- # Remove this line when this test image is regenerated.
- plt.rcParams['text.kerning_factor'] = 6
- get_ax()
- plt.subplots_adjust(left=0.3)
- rax1 = plt.axes([0.05, 0.7, 0.15, 0.15])
- rax2 = plt.axes([0.05, 0.2, 0.15, 0.15])
- widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3'))
- widgets.CheckButtons(rax2, ('Check 1', 'Check 2', 'Check 3'),
- (False, True, True))
- @image_comparison(['check_bunch_of_radio_buttons.png'],
- style='mpl20', remove_text=True)
- def test_check_bunch_of_radio_buttons():
- rax = plt.axes([0.05, 0.1, 0.15, 0.7])
- widgets.RadioButtons(rax, ('B1', 'B2', 'B3', 'B4', 'B5', 'B6',
- 'B7', 'B8', 'B9', 'B10', 'B11', 'B12',
- 'B13', 'B14', 'B15'))
- def test_slider_slidermin_slidermax_invalid():
- fig, ax = plt.subplots()
- # test min/max with floats
- with pytest.raises(ValueError):
- widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
- slidermin=10.0)
- with pytest.raises(ValueError):
- widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
- slidermax=10.0)
- def test_slider_slidermin_slidermax():
- fig, ax = plt.subplots()
- slider_ = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
- valinit=5.0)
- slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
- valinit=1.0, slidermin=slider_)
- assert slider.val == slider_.val
- slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
- valinit=10.0, slidermax=slider_)
- assert slider.val == slider_.val
- def test_slider_valmin_valmax():
- fig, ax = plt.subplots()
- slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
- valinit=-10.0)
- assert slider.val == slider.valmin
- slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
- valinit=25.0)
- assert slider.val == slider.valmax
- def test_slider_horizontal_vertical():
- fig, ax = plt.subplots()
- slider = widgets.Slider(ax=ax, label='', valmin=0, valmax=24,
- valinit=12, orientation='horizontal')
- slider.set_val(10)
- assert slider.val == 10
- # check the dimension of the slider patch in axes units
- box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
- assert_allclose(box.bounds, [0, 0, 10/24, 1])
- fig, ax = plt.subplots()
- slider = widgets.Slider(ax=ax, label='', valmin=0, valmax=24,
- valinit=12, orientation='vertical')
- slider.set_val(10)
- assert slider.val == 10
- # check the dimension of the slider patch in axes units
- box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
- assert_allclose(box.bounds, [0, 0, 1, 10/24])
- def check_polygon_selector(event_sequence, expected_result, selections_count):
- """Helper function to test Polygon Selector
- Parameters
- ----------
- event_sequence : list of tuples (etype, dict())
- A sequence of events to perform. The sequence is a list of tuples
- where the first element of the tuple is an etype (e.g., 'onmove',
- 'press', etc.), and the second element of the tuple is a dictionary of
- the arguments for the event (e.g., xdata=5, key='shift', etc.).
- expected_result : list of vertices (xdata, ydata)
- The list of vertices that are expected to result from the event
- sequence.
- selections_count : int
- Wait for the tool to call its `onselect` function `selections_count`
- times, before comparing the result to the `expected_result`
- """
- ax = get_ax()
- ax._selections_count = 0
- def onselect(vertices):
- ax._selections_count += 1
- ax._current_result = vertices
- tool = widgets.PolygonSelector(ax, onselect)
- for (etype, event_args) in event_sequence:
- do_event(tool, etype, **event_args)
- assert ax._selections_count == selections_count
- assert ax._current_result == expected_result
- def polygon_place_vertex(xdata, ydata):
- return [('onmove', dict(xdata=xdata, ydata=ydata)),
- ('press', dict(xdata=xdata, ydata=ydata)),
- ('release', dict(xdata=xdata, ydata=ydata))]
- def test_polygon_selector():
- # Simple polygon
- expected_result = [(50, 50), (150, 50), (50, 150)]
- event_sequence = (polygon_place_vertex(50, 50)
- + polygon_place_vertex(150, 50)
- + polygon_place_vertex(50, 150)
- + polygon_place_vertex(50, 50))
- check_polygon_selector(event_sequence, expected_result, 1)
- # Move first vertex before completing the polygon.
- expected_result = [(75, 50), (150, 50), (50, 150)]
- event_sequence = (polygon_place_vertex(50, 50)
- + polygon_place_vertex(150, 50)
- + [('on_key_press', dict(key='control')),
- ('onmove', dict(xdata=50, ydata=50)),
- ('press', dict(xdata=50, ydata=50)),
- ('onmove', dict(xdata=75, ydata=50)),
- ('release', dict(xdata=75, ydata=50)),
- ('on_key_release', dict(key='control'))]
- + polygon_place_vertex(50, 150)
- + polygon_place_vertex(75, 50))
- check_polygon_selector(event_sequence, expected_result, 1)
- # Move first two vertices at once before completing the polygon.
- expected_result = [(50, 75), (150, 75), (50, 150)]
- event_sequence = (polygon_place_vertex(50, 50)
- + polygon_place_vertex(150, 50)
- + [('on_key_press', dict(key='shift')),
- ('onmove', dict(xdata=100, ydata=100)),
- ('press', dict(xdata=100, ydata=100)),
- ('onmove', dict(xdata=100, ydata=125)),
- ('release', dict(xdata=100, ydata=125)),
- ('on_key_release', dict(key='shift'))]
- + polygon_place_vertex(50, 150)
- + polygon_place_vertex(50, 75))
- check_polygon_selector(event_sequence, expected_result, 1)
- # Move first vertex after completing the polygon.
- expected_result = [(75, 50), (150, 50), (50, 150)]
- event_sequence = (polygon_place_vertex(50, 50)
- + polygon_place_vertex(150, 50)
- + polygon_place_vertex(50, 150)
- + polygon_place_vertex(50, 50)
- + [('onmove', dict(xdata=50, ydata=50)),
- ('press', dict(xdata=50, ydata=50)),
- ('onmove', dict(xdata=75, ydata=50)),
- ('release', dict(xdata=75, ydata=50))])
- check_polygon_selector(event_sequence, expected_result, 2)
- # Move all vertices after completing the polygon.
- expected_result = [(75, 75), (175, 75), (75, 175)]
- event_sequence = (polygon_place_vertex(50, 50)
- + polygon_place_vertex(150, 50)
- + polygon_place_vertex(50, 150)
- + polygon_place_vertex(50, 50)
- + [('on_key_press', dict(key='shift')),
- ('onmove', dict(xdata=100, ydata=100)),
- ('press', dict(xdata=100, ydata=100)),
- ('onmove', dict(xdata=125, ydata=125)),
- ('release', dict(xdata=125, ydata=125)),
- ('on_key_release', dict(key='shift'))])
- check_polygon_selector(event_sequence, expected_result, 2)
- # Try to move a vertex and move all before placing any vertices.
- expected_result = [(50, 50), (150, 50), (50, 150)]
- event_sequence = ([('on_key_press', dict(key='control')),
- ('onmove', dict(xdata=100, ydata=100)),
- ('press', dict(xdata=100, ydata=100)),
- ('onmove', dict(xdata=125, ydata=125)),
- ('release', dict(xdata=125, ydata=125)),
- ('on_key_release', dict(key='control')),
- ('on_key_press', dict(key='shift')),
- ('onmove', dict(xdata=100, ydata=100)),
- ('press', dict(xdata=100, ydata=100)),
- ('onmove', dict(xdata=125, ydata=125)),
- ('release', dict(xdata=125, ydata=125)),
- ('on_key_release', dict(key='shift'))]
- + polygon_place_vertex(50, 50)
- + polygon_place_vertex(150, 50)
- + polygon_place_vertex(50, 150)
- + polygon_place_vertex(50, 50))
- check_polygon_selector(event_sequence, expected_result, 1)
- # Try to place vertex out-of-bounds, then reset, and start a new polygon.
- expected_result = [(50, 50), (150, 50), (50, 150)]
- event_sequence = (polygon_place_vertex(50, 50)
- + polygon_place_vertex(250, 50)
- + [('on_key_press', dict(key='escape')),
- ('on_key_release', dict(key='escape'))]
- + polygon_place_vertex(50, 50)
- + polygon_place_vertex(150, 50)
- + polygon_place_vertex(50, 150)
- + polygon_place_vertex(50, 50))
- check_polygon_selector(event_sequence, expected_result, 1)
|