Подтвердить что ты не робот

RectangleSelector исчезает при масштабировании

Когда я запускаю этот пример и создаю прямоугольный выбор, если я увеличиваю или перемещаю окно графика вокруг выделения, исчезает, пока я не отменим выбор или инструмент масштабирования и снова щелкните по окну графика.

Я использую %matplotlib tkinter в ноутбуке IPython.

Я попытался подключиться к ограничениям, которые возникают при увеличении окна и выборе видимости прямоугольника:

def persist_rect(newlims):
    rs = toggle_selector.RS
    print(rs.visible)
    rs.set_visible(True)
    rs.update()

current_ax.callbacks.connect('xlim_changed', persist_rect)
current_ax.callbacks.connect('ylim_changed', persist_rect)

Но это ничего не делает. Даже не кажется, что toggle_selector.RS.visible всегда установлено в false.

Я также смотрел источник для RectangleSelector, но я ничего не видел там.

Я также обнаружил, что у меня есть эта проблема, когда я изменяю размер выделенной области с помощью RectangleSelector.extents = new_extents. Когда .extents изменяется, например, с помощью виджета слайдера, выбранная область исчезает, пока я не нажму на график снова.

Все эти проблемы проблемы исчезают, если RectangleSelector инициализируется useblit=False, как предлагает @ImportanceOfBeingErnest, но, как говорится, это не очень эффективное решение.

4b9b3361

Ответ 1

Добавление обратного вызова для draw_event s:

def mycallback(event):
    if RS.active:
        RS.update()
plt.connect('draw_event', mycallback)

заставляет RectangleSelector сохраняться после масштабирования или панорамирования и совместим с useblit=True.


Например, используя код из документов в качестве базы:

from __future__ import print_function
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.widgets as widgets
import threading
import datetime as DT

def line_select_callback(eclick, erelease):
    'eclick and erelease are the press and release events'
    x1, y1 = eclick.xdata, eclick.ydata
    x2, y2 = erelease.xdata, erelease.ydata
    print("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2))
    print(" The button you used were: %s %s" % (eclick.button, erelease.button))

def toggle_selector(event):
    print(' Key pressed: {}'.format(event.key))
    if event.key in ['D', 'd'] and RS.active:
        print(' RectangleSelector deactivated.')
        RS.set_active(False)
        RS.set_visible(False)
        RS.update()
    if event.key in ['A', 'a'] and not RS.active:
        print(' RectangleSelector activated.')
        RS.set_active(True)
        RS.set_visible(True)
        RS.update()

def mycallback(event):
    if RS.active:
        # print('mycallback')
        RS.update()

# def persist_rect(newlims):
#     print('persist_rect')
#     RS.set_visible(True)
#     RS.update()

fig, ax = plt.subplots() 
# figtype = type(fig)
# figtype._draw = figtype.draw
# def mydraw(self, renderer):
#     print('figure.draw')
#     self._draw(renderer)
# figtype.draw = mydraw

N = 100000               
x = np.linspace(0.0, 10.0, N) 

RS = RectangleSelector(ax, line_select_callback,
                       drawtype='box', useblit=True,
                       button=[1, 3],  # don't use middle button
                       minspanx=5, minspany=5,
                       spancoords='pixels',
                       interactive=True)

plt.plot(x, +np.sin(.2*np.pi*x), lw=3.5, c='b', alpha=.7) 
plt.plot(x, +np.cos(.2*np.pi*x), lw=3.5, c='r', alpha=.5)
plt.plot(x, -np.sin(.2*np.pi*x), lw=3.5, c='g', alpha=.3)

plt.connect('key_press_event', toggle_selector)
plt.connect('draw_event', mycallback)
# ax.callbacks.connect('xlim_changed', persist_rect)
# ax.callbacks.connect('ylim_changed', persist_rect)

plt.show()

Почему работает mycallback, но persist_rect нет?

Если вы раскомментируете приведенные выше заявления, вы получите распечатку, которая будет выглядеть примерно так:

figure.draw
mycallback
figure.draw
mycallback
(4.09, -0.53) --> (8.15, 0.38)
 The button you used were: 1 1
persist_rect
persist_rect
figure.draw
mycallback
 Key pressed: q

Обратите внимание, что persist_rect вызывается до figure.draw, а mycallback вызывается потом. figure.draw не рисует RectangleSelection, но рисует Rectangle, используемый для фона. Итак, figure.draw скрывает RectangleSelection. Таким образом, persist_rect на мгновение отображает RectangleSelection, но он не сохраняется. mycallback работает, потому что он вызывается после figure.draw.

Ответ 2

Если я правильно понимаю, селектор прямоугольника должен оставаться видимым во время процесса панорамирования или масштабирования. Этого можно добиться, если не использовать blitting,

toggle_selector.RS = RectangleSelector(ax, ...,  useblit=False, ...)

Побочным эффектом этого является то, что построение графика может замедляться в зависимости от сложности графика, так как без блиттинга полный график непрерывно перерисовывается при использовании селектора прямоугольников.

Ответ 3

В исходном коде RectangularSelector метод выпуска (line 2119)  обрабатывает видимость селектора

def _release(self, event):   
"""on button release event"""
    if not self.interactive:
        self.to_draw.set_visible(False)

Подкласс RectangleSelector для изменения способа выпуска

class visibleRectangleSelector(RectangleSelector):
    def release(self, event):
        super(visibleRectangleSelector, self).release(event)
        self.to_draw.set_visible(True)
        self.canvas.draw()   ##updates canvas for new selection

Пример кода с использованием doc example

from __future__ import print_function
"""
Do a mouseclick somewhere, move the mouse to some destination, release
the button.  This class gives click- and release-events and also draws
a line or a box from the click-point to the actual mouseposition
(within the same axes) until the button is released.  Within the
method 'self.ignore()' it is checked whether the button from eventpress
and eventrelease are the same.

"""
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib.pyplot as plt


class visibleRectangleSelector(RectangleSelector):
    def release(self, event):
        super(visibleRectangleSelector, self).release(event)
        self.to_draw.set_visible(True)
        self.canvas.draw() 


def line_select_callback(eclick, erelease):
    'eclick and erelease are the press and release events'
    x1, y1 = eclick.xdata, eclick.ydata
    x2, y2 = erelease.xdata, erelease.ydata
    print("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2))
    print(" The button you used were: %s %s" % (eclick.button,
                                                erelease.button))


def toggle_selector(event):
    print(' Key pressed.')
    if event.key in ['Q', 'q'] and toggle_selector.RS.active:
        print(' RectangleSelector deactivated.')
        toggle_selector.RS.set_active(False)
    if event.key in ['A', 'a'] and not toggle_selector.RS.active:
        print(' RectangleSelector activated.')
        toggle_selector.RS.set_active(True)


fig, current_ax = plt.subplots()  # make a new plotting range
N = 100000  # If N is large one can see
x = np.linspace(0.0, 10.0, N)  # improvement by use blitting!

plt.plot(x, +np.sin(.2 * np.pi * x), lw=3.5, c='b', alpha=.7)  # plot something
plt.plot(x, +np.cos(.2 * np.pi * x), lw=3.5, c='r', alpha=.5)
plt.plot(x, -np.sin(.2 * np.pi * x), lw=3.5, c='g', alpha=.3)

print("\n      click  -->  release")

# drawtype is 'box' or 'line' or 'none'
toggle_selector.RS = RectangleSelector(
    current_ax,
    line_select_callback,
    drawtype='box',
    useblit=False,
    button=[1, 3],  # don't use middle button
    minspanx=5,
    minspany=5,
    spancoords='pixels',
    interactive=True)
plt.connect('key_press_event', toggle_selector)
plt.show()