矩形选择器在缩放时消失
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_event
s 添加回调:
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_rect
在figure.draw
之前被调用,而mycallback
在之后被调用。 figure.draw
不绘制RectangleSelection
,但它绘制了用于背景的Rectangle
。所以figure.draw
掩盖了RectangleSelection
。
因此persist_rect
暂时显示RectangleSelection
,但它无法持续存在。
mycallback
有效,因为它是在 figure.draw
之后调用的。
【讨论】:
确实!对此有什么好的解释吗?原则上,这非常接近 OP 在限制更改回调时所尝试的(并且这不起作用)。此外,调用.update
似乎也不直观,它实际上会在绘制事件的回调上绘制矩形。感觉有点像蛇咬自己的尾巴,但它显然在起作用。
我认为这归结为相对于figure.draw
调用回调的顺序。 persist_rect
在 figure.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中的日期选择器在第二页中添加时重复