问题
I am working on a project on which I want to select an area on an image in order to do some treatments on this area. So I found this wonderful tool RectangleSelector of matplotlib. But what I want is setting the rectangle at an initial position on the image, instead of waiting for the user clicking a first area (for example a rectangle of 10x10 pixels on top/left of the image).
At first I thought I have to use state_modifier_keys
but it doesn't seem to be this.
So, is it possible ?
An example (with plots but it is the same question) can be:
from __future__ import print_function
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib.pyplot as plt
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=True,
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()
回答1:
Without blitting
If you're willing not to use blitting, the solution would be to set the RectangleSelector
's patch visible (RS.to_draw.set_visible(True)
) and set its extent to the data you want it to be (RS.extents = (0,4,0,1)
).
from __future__ import print_function
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib.pyplot as plt
def line_select_callback(eclick, erelease):
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))
fig, current_ax = plt.subplots()
N = 100000
x = np.linspace(0.0, 10.0, N)
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)
# drawtype is 'box' or 'line' or 'none'
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)
RS.to_draw.set_visible(True)
fig.canvas.draw()
RS.extents = (0,4,0,1)
plt.show()
With blitting
The above will unfortunately not work when blitting is used.
A solution with blitting would need to involve copying the background, then setting the rectangle visible, but setting the animated
property to false. Then for the first time the canvas is drawn the rectangle is shown. However for later use we would need to set the animated
property to True again. Hence for the first time a click happens inside the figure, we need to set the animated
property to True and blit the canvas with a background which has been saved before showing the rectangle.
from __future__ import print_function
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib.pyplot as plt
def line_select_callback(eclick, erelease):
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))
fig, current_ax = plt.subplots()
N = 100000
x = np.linspace(0.0, 10.0, N)
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)
# drawtype is 'box' or 'line' or 'none'
RS = RectangleSelector(current_ax, line_select_callback,
drawtype='box', useblit=True,
button=[1, 3], # don't use middle button
minspanx=5, minspany=5,
spancoords='pixels',
interactive=True)
fig.canvas.draw()
bg = fig.canvas.copy_from_bbox(RS.ax.bbox)
RS.set_visible(True)
ext = (0,4,0,1)
RS.draw_shape(ext)
# Update displayed handles
RS._corner_handles.set_data(*RS.corners)
RS._edge_handles.set_data(*RS.edge_centers)
RS._center_handle.set_data(*RS.center)
for artist in RS.artists:
RS.ax.draw_artist(artist)
artist.set_animated(False)
fig.canvas.draw()
def initclick(evt):
RS.background = bg
RS.update()
for artist in RS.artists:
artist.set_animated(True)
fig.canvas.mpl_disconnect(cid)
cid = fig.canvas.mpl_connect("button_press_event",initclick)
plt.show()
来源:https://stackoverflow.com/questions/47529558/matplotlib-rectangleselector-set-initial-position