RectangleSelector 在缩放时消失

RectangleSelector Disappears on Zoom

当我 运行 this 示例并创建一个矩形选区时,如果我缩放或移动绘图 window 选区周围的选区会消失,直到我取消选择移动或缩放工具并单击情节再次 window。

我正在 IPython 笔记本中使用 %matplotlib tkinter

我试图了解 window 缩放时发生的限制变化并将矩形选区设置为可见:

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。

我也一直在看 source for RectangleSelector,但我没有看到任何有启发性的东西。

我还发现,当我使用 RectangleSelector.extents = new_extents 修改所选区域的范围时,我也遇到了这个问题。当 .extents 被修改时,例如使用滑块小部件,所选区域会消失,直到我再次单击绘图。

如果按照@ImportanceOfBeingErnest 的建议用 useblit=False 初始化 RectangleSelector,所有这些问题都会消失,但是,正如他们所说,这不是一个非常高效的解决方案。

如果我没理解错的话,矩形选择器应该在整个平移或缩放过程中保持可见。这可以通过不使用 blitting 来实现,

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

这样做的一个副作用是绘图可能会变慢,具体取决于绘图的复杂程度,因为如果不使用 blitting,则在使用矩形选择器时会不断重绘整个绘图。

RectangularSelector the release method (line 2119 的源代码中) 处理选择器的可见性

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

子类RectangleSelector修改release方法

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 示例

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()

draw_event 秒添加回调:

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

使 RectangleSelector 在缩放或平移后保持不变,并与 useblit=True 兼容。


例如,使用code from the docs作为基础:

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_rectfigure.draw 之前被调用,而 mycallback 在之后被调用。 figure.draw 不绘制 RectangleSelection,但绘制用于背景的 Rectangle。所以 figure.draw 掩盖了 RectangleSelection。 因此 persist_rect 暂时显示 RectangleSelection,但无法持续。 mycallback 有效,因为它是在 figure.draw.

之后调用的