矩形选择器在缩放时消失

Posted

技术标签:

【中文标题】矩形选择器在缩放时消失【英文标题】:RectangleSelector Disappears on Zoom 【发布时间】:2018-01-13 19:55:44 【问题描述】:

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

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

我已尝试使用缩放窗口时发生的限制更改并将矩形选择设置为可见:

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 被修改时,例如使用滑块小部件,所选区域会消失,直到我再次单击绘图。

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

【问题讨论】:

你检查过这个问题的答案吗:***.com/questions/34517484/…? @JafferWilson:感谢您指出这个问题。不幸的是,答案不起作用。按原样使用时,矩形会立即消失。如果与interactive=True 一起使用,仍然存在与上述问题相同的问题。 【参考方案1】:

draw_events 添加回调:

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 之后调用的。

【讨论】:

确实!对此有什么好的解释吗?原则上,这非常接近 OP 在限制更改回调时所尝试的(并且这不起作用)。此外,调用.update 似乎也不直观,它实际上会在绘制事件的回调上绘制矩形。感觉有点像蛇咬自己的尾巴,但它显然在起作用。 我认为这归结为相对于figure.draw 调用回调的顺序。 persist_rectfigure.draw 之前被调用,而 mycallback 在之后被调用。由于figure.draw 绘制在RectangleSelector 之上,persist_rect 无法保持RectangleSelector 持续可见。我在上面添加了一些猴子补丁代码来显示figure.draw 何时被调用。【参考方案2】:

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

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

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

【讨论】:

感谢您的帮助,不幸的是,这对我来说不是最佳解决方案。我想使用矩形选择器来选择使用imshow 绘制的瀑布图上的区域。这些瀑布阵列中最小的约为 18MB,最大的约为 220MB。 RectangleSelector 与 blitting 配合得非常好,除了这个重绘问题。有什么办法可以在缩放/移动后手动强制它重绘? 这对于简单的情节非常有效。当我有足够的代表时,我会很高兴 +1。【参考方案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 示例的示例代码

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

【讨论】:

感谢您调查此问题。然而,这个解决方案似乎只是因为你使用了blit=False,就像my answer一样。使用blit=False 时,根本不需要子类化。使用blit=True 尝试此解决方案时,RectangleSelector 仍然消失。所以它似乎并没有解决问题。 从上面@JafferWilson 的评论中可以看出,这个答案是this question 的答案的抄袭

以上是关于矩形选择器在缩放时消失的主要内容,如果未能解决你的问题,请参考以下文章

NSComboBox 动作选择器在 setHidden:YES 时触发

选择器在数据更改时删除选定的段

内联日期选择器在第二次尝试时崩溃

jquery mobile中的日期选择器在第二页中添加时重复

SwiftUI 选择器在 ObservedObject 公共变量更新期间滚动时跳转

没有找到选择器的节点,但选择器在 HTML 页面上