Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenDocCN
matplotlib-doc-zh
提交
ab95ecde
M
matplotlib-doc-zh
项目概览
OpenDocCN
/
matplotlib-doc-zh
8 个月 前同步成功
通知
0
Star
192
Fork
111
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
matplotlib-doc-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
ab95ecde
编写于
1月 26, 2017
作者:
W
wizardforcel
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
7.3
上级
7dacf0d1
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
480 addition
and
0 deletion
+480
-0
7.3.md
7.3.md
+480
-0
未找到文件。
7.3.md
0 → 100644
浏览文件 @
ab95ecde
# 事件处理及拾取
> 原文:[Event handling and picking](http://matplotlib.org/users/event_handling.html)
> 译者:[飞龙](https://github.com/)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
matplotlib 使用了许多用户界面工具包(wxpython,tkinter,qt4,gtk 和 macosx),为了支持交互式平移和缩放图形等功能,拥有一套 API 通过按键和鼠标移动与图形交互,并且『GUI中立』,对开发人员十分有帮助,所以我们不必重复大量的代码来跨不同的用户界面。虽然事件处理 API 是 GUI 中立的,但它是基于 GTK 模型,这是 matplotlib 支持的第一个用户界面。与标准 GUI 事件相比,被触发的事件也比 matplotlib 丰富一些,例如包括发生事件的
`matplotlib.axes.Axes`
的信息。事件还能够理解 matplotlib 坐标系,并且在事件中以像素和数据坐标为单位报告事件位置。
## 事件连接
要接收事件,你需要编写一个回调函数,然后将你的函数连接到事件管理器,它是
`FigureCanvasBase`
的一部分。这是一个简单的例子,打印鼠标点击的位置和按下哪个按钮:
```
py
fig
=
plt
.
figure
()
ax
=
fig
.
add_subplot
(
111
)
ax
.
plot
(
np
.
random
.
rand
(
10
))
def
onclick
(
event
):
print
(
'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'
%
(
event
.
button
,
event
.
x
,
event
.
y
,
event
.
xdata
,
event
.
ydata
))
cid
=
fig
.
canvas
.
mpl_connect
(
'button_press_event'
,
onclick
)
```
`FigureCanvas`
的方法
`mpl_connect()`
返回一个连接
`id`
,它只是一个整数。 当你要断开回调时,只需调用:
```
py
fig
.
canvas
.
mpl_disconnect
(
cid
)
```
> 注意
> 画布仅保留回调的弱引用。 因此,如果回调是类实例的方法,你需要保留对该实例的引用。 否则实例将被垃圾回收,回调将消失。
以下是可以连接到的事件,在事件发生时发回给你的类实例以及事件描述:
| 事件名称 | 类和描述 |
| --- | --- | --- |
|
`'button_press_event'`
|
`MouseEvent`
- 鼠标按钮被按下 |
|
`'button_release_event'`
|
`MouseEvent`
- 鼠标按钮被释放 |
|
`'draw_event'`
|
`DrawEvent`
- 画布绘图 |
|
`'key_press_event'`
|
`KeyEvent`
- 按键被按下 |
|
`'key_release_event'`
|
`KeyEvent`
- 按键被释放 |
|
`'motion_notify_event'`
|
`MouseEvent`
- 鼠标移动 |
|
`'pick_event'`
|
`PickEvent`
- 画布中的对象被选中 |
|
`'resize_event'`
|
`ResizeEvent`
- 图形画布大小改变 |
|
`'scroll_event'`
|
`MouseEvent`
- 鼠标滚轮被滚动 |
|
`'figure_enter_event'`
|
`LocationEvent`
- 鼠标进入新的图形 |
|
`'figure_leave_event'`
|
`LocationEvent`
- 鼠标离开图形 |
|
`'axes_enter_event'`
|
`LocationEvent`
- 鼠标进入新的轴域 |
|
`'axes_leave_event'`
|
`LocationEvent`
- 鼠标离开轴域 |
## 事件属性
所有 matplotlib 事件继承自基类
`matplotlib.backend_bases.Event`
,储存以下属性:
`name`
事件名称
`canvas`
生成事件的
`FigureCanvas `
实例
`guiEvent`
触发 matplotlib 事件的 GUI 事件
最常见的事件是按键按下/释放事件、鼠标按下/释放和移动事件。 处理这些事件的
`KeyEvent`
和
`MouseEvent`
类都派生自
`LocationEvent`
,它具有以下属性:
`x`
x 位置,距离画布左端的像素
`y`
y 位置,距离画布底端的像素
`inaxes`
如果鼠标经过轴域,则为
`Axes`
实例
`xdata`
鼠标的
`x`
坐标,以数据坐标为单位
`ydata`
鼠标的
`y`
坐标,以数据坐标为单位
但我们看一看画布的简单示例,其中每次按下鼠标时都会创建一条线段。
```
py
from
matplotlib
import
pyplot
as
plt
class
LineBuilder
:
def
__init__
(
self
,
line
):
self
.
line
=
line
self
.
xs
=
list
(
line
.
get_xdata
())
self
.
ys
=
list
(
line
.
get_ydata
())
self
.
cid
=
line
.
figure
.
canvas
.
mpl_connect
(
'button_press_event'
,
self
)
def
__call__
(
self
,
event
):
print
(
'click'
,
event
)
if
event
.
inaxes
!=
self
.
line
.
axes
:
return
self
.
xs
.
append
(
event
.
xdata
)
self
.
ys
.
append
(
event
.
ydata
)
self
.
line
.
set_data
(
self
.
xs
,
self
.
ys
)
self
.
line
.
figure
.
canvas
.
draw
()
fig
=
plt
.
figure
()
ax
=
fig
.
add_subplot
(
111
)
ax
.
set_title
(
'click to build line segments'
)
line
,
=
ax
.
plot
([
0
],
[
0
])
# empty line
linebuilder
=
LineBuilder
(
line
)
plt
.
show
()
```
我们刚刚使用的
`MouseEvent`
是一个
`LocationEvent`
,因此我们可以访问
`event.x`
和
`event.xdata`
中的数据和像素坐标。 除了
`LocationEvent`
属性,它拥有:
`button`
按下的按钮,
`None`
、1、2、3、
`'up'`
、
`'down'`
(
`'up'`
、
`'down'`
用于滚动事件)
`key`
按下的键,
`None`
,任何字符,
`'shift'`
、
`'win'`
或者
`'control'`
### 可拖拽的矩形练习
编写使用
`Rectangle`
实例初始化的可拖动矩形类,但在拖动时会移动其
`x`
,
`y`
位置。 提示:你需要存储矩形的原始
`xy`
位置,存储为
`rect.xy`
并连接到按下,移动和释放鼠标事件。 当鼠标按下时,检查点击是否发生在你的矩形上(见
`matplotlib.patches.Rectangle.contains()`
),如果是,存储矩形
`xy`
和数据坐标为单位的鼠标点击位置。 在移动事件回调中,计算鼠标移动的
`deltax`
和
`deltay`
,并将这些增量添加到存储的原始矩形,并重新绘图。 在按钮释放事件中,只需将所有你存储的按钮按下数据重置为
`None`
。
这里是解决方案:
```
py
import
numpy
as
np
import
matplotlib.pyplot
as
plt
class
DraggableRectangle
:
def
__init__
(
self
,
rect
):
self
.
rect
=
rect
self
.
press
=
None
def
connect
(
self
):
'connect to all the events we need'
self
.
cidpress
=
self
.
rect
.
figure
.
canvas
.
mpl_connect
(
'button_press_event'
,
self
.
on_press
)
self
.
cidrelease
=
self
.
rect
.
figure
.
canvas
.
mpl_connect
(
'button_release_event'
,
self
.
on_release
)
self
.
cidmotion
=
self
.
rect
.
figure
.
canvas
.
mpl_connect
(
'motion_notify_event'
,
self
.
on_motion
)
def
on_press
(
self
,
event
):
'on button press we will see if the mouse is over us and store some data'
if
event
.
inaxes
!=
self
.
rect
.
axes
:
return
contains
,
attrd
=
self
.
rect
.
contains
(
event
)
if
not
contains
:
return
print
(
'event contains'
,
self
.
rect
.
xy
)
x0
,
y0
=
self
.
rect
.
xy
self
.
press
=
x0
,
y0
,
event
.
xdata
,
event
.
ydata
def
on_motion
(
self
,
event
):
'on motion we will move the rect if the mouse is over us'
if
self
.
press
is
None
:
return
if
event
.
inaxes
!=
self
.
rect
.
axes
:
return
x0
,
y0
,
xpress
,
ypress
=
self
.
press
dx
=
event
.
xdata
-
xpress
dy
=
event
.
ydata
-
ypress
#print('x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f' %
# (x0, xpress, event.xdata, dx, x0+dx))
self
.
rect
.
set_x
(
x0
+
dx
)
self
.
rect
.
set_y
(
y0
+
dy
)
self
.
rect
.
figure
.
canvas
.
draw
()
def
on_release
(
self
,
event
):
'on release we reset the press data'
self
.
press
=
None
self
.
rect
.
figure
.
canvas
.
draw
()
def
disconnect
(
self
):
'disconnect all the stored connection ids'
self
.
rect
.
figure
.
canvas
.
mpl_disconnect
(
self
.
cidpress
)
self
.
rect
.
figure
.
canvas
.
mpl_disconnect
(
self
.
cidrelease
)
self
.
rect
.
figure
.
canvas
.
mpl_disconnect
(
self
.
cidmotion
)
fig
=
plt
.
figure
()
ax
=
fig
.
add_subplot
(
111
)
rects
=
ax
.
bar
(
range
(
10
),
20
*
np
.
random
.
rand
(
10
))
drs
=
[]
for
rect
in
rects
:
dr
=
DraggableRectangle
(
rect
)
dr
.
connect
()
drs
.
append
(
dr
)
plt
.
show
()
```
附加题:使用动画秘籍中讨论的动画 blit 技术,使动画绘制更快更流畅。
附加题解决方案:
```
py
# draggable rectangle with the animation blit techniques; see
# http://www.scipy.org/Cookbook/Matplotlib/Animations
import
numpy
as
np
import
matplotlib.pyplot
as
plt
class
DraggableRectangle
:
lock
=
None
# only one can be animated at a time
def
__init__
(
self
,
rect
):
self
.
rect
=
rect
self
.
press
=
None
self
.
background
=
None
def
connect
(
self
):
'connect to all the events we need'
self
.
cidpress
=
self
.
rect
.
figure
.
canvas
.
mpl_connect
(
'button_press_event'
,
self
.
on_press
)
self
.
cidrelease
=
self
.
rect
.
figure
.
canvas
.
mpl_connect
(
'button_release_event'
,
self
.
on_release
)
self
.
cidmotion
=
self
.
rect
.
figure
.
canvas
.
mpl_connect
(
'motion_notify_event'
,
self
.
on_motion
)
def
on_press
(
self
,
event
):
'on button press we will see if the mouse is over us and store some data'
if
event
.
inaxes
!=
self
.
rect
.
axes
:
return
if
DraggableRectangle
.
lock
is
not
None
:
return
contains
,
attrd
=
self
.
rect
.
contains
(
event
)
if
not
contains
:
return
print
(
'event contains'
,
self
.
rect
.
xy
)
x0
,
y0
=
self
.
rect
.
xy
self
.
press
=
x0
,
y0
,
event
.
xdata
,
event
.
ydata
DraggableRectangle
.
lock
=
self
# draw everything but the selected rectangle and store the pixel buffer
canvas
=
self
.
rect
.
figure
.
canvas
axes
=
self
.
rect
.
axes
self
.
rect
.
set_animated
(
True
)
canvas
.
draw
()
self
.
background
=
canvas
.
copy_from_bbox
(
self
.
rect
.
axes
.
bbox
)
# now redraw just the rectangle
axes
.
draw_artist
(
self
.
rect
)
# and blit just the redrawn area
canvas
.
blit
(
axes
.
bbox
)
def
on_motion
(
self
,
event
):
'on motion we will move the rect if the mouse is over us'
if
DraggableRectangle
.
lock
is
not
self
:
return
if
event
.
inaxes
!=
self
.
rect
.
axes
:
return
x0
,
y0
,
xpress
,
ypress
=
self
.
press
dx
=
event
.
xdata
-
xpress
dy
=
event
.
ydata
-
ypress
self
.
rect
.
set_x
(
x0
+
dx
)
self
.
rect
.
set_y
(
y0
+
dy
)
canvas
=
self
.
rect
.
figure
.
canvas
axes
=
self
.
rect
.
axes
# restore the background region
canvas
.
restore_region
(
self
.
background
)
# redraw just the current rectangle
axes
.
draw_artist
(
self
.
rect
)
# blit just the redrawn area
canvas
.
blit
(
axes
.
bbox
)
def
on_release
(
self
,
event
):
'on release we reset the press data'
if
DraggableRectangle
.
lock
is
not
self
:
return
self
.
press
=
None
DraggableRectangle
.
lock
=
None
# turn off the rect animation property and reset the background
self
.
rect
.
set_animated
(
False
)
self
.
background
=
None
# redraw the full figure
self
.
rect
.
figure
.
canvas
.
draw
()
def
disconnect
(
self
):
'disconnect all the stored connection ids'
self
.
rect
.
figure
.
canvas
.
mpl_disconnect
(
self
.
cidpress
)
self
.
rect
.
figure
.
canvas
.
mpl_disconnect
(
self
.
cidrelease
)
self
.
rect
.
figure
.
canvas
.
mpl_disconnect
(
self
.
cidmotion
)
fig
=
plt
.
figure
()
ax
=
fig
.
add_subplot
(
111
)
rects
=
ax
.
bar
(
range
(
10
),
20
*
np
.
random
.
rand
(
10
))
drs
=
[]
for
rect
in
rects
:
dr
=
DraggableRectangle
(
rect
)
dr
.
connect
()
drs
.
append
(
dr
)
plt
.
show
()
```
## 鼠标进入和离开
如果希望在鼠标进入或离开图形时通知你,你可以连接到图形/轴域进入/离开事件。 下面是一个简单的例子,它改变了鼠标所在的轴域和图形的背景颜色:
```
py
"""
Illustrate the figure and axes enter and leave events by changing the
frame colors on enter and leave
"""
import
matplotlib.pyplot
as
plt
def
enter_axes
(
event
):
print
(
'enter_axes'
,
event
.
inaxes
)
event
.
inaxes
.
patch
.
set_facecolor
(
'yellow'
)
event
.
canvas
.
draw
()
def
leave_axes
(
event
):
print
(
'leave_axes'
,
event
.
inaxes
)
event
.
inaxes
.
patch
.
set_facecolor
(
'white'
)
event
.
canvas
.
draw
()
def
enter_figure
(
event
):
print
(
'enter_figure'
,
event
.
canvas
.
figure
)
event
.
canvas
.
figure
.
patch
.
set_facecolor
(
'red'
)
event
.
canvas
.
draw
()
def
leave_figure
(
event
):
print
(
'leave_figure'
,
event
.
canvas
.
figure
)
event
.
canvas
.
figure
.
patch
.
set_facecolor
(
'grey'
)
event
.
canvas
.
draw
()
fig1
=
plt
.
figure
()
fig1
.
suptitle
(
'mouse hover over figure or axes to trigger events'
)
ax1
=
fig1
.
add_subplot
(
211
)
ax2
=
fig1
.
add_subplot
(
212
)
fig1
.
canvas
.
mpl_connect
(
'figure_enter_event'
,
enter_figure
)
fig1
.
canvas
.
mpl_connect
(
'figure_leave_event'
,
leave_figure
)
fig1
.
canvas
.
mpl_connect
(
'axes_enter_event'
,
enter_axes
)
fig1
.
canvas
.
mpl_connect
(
'axes_leave_event'
,
leave_axes
)
fig2
=
plt
.
figure
()
fig2
.
suptitle
(
'mouse hover over figure or axes to trigger events'
)
ax1
=
fig2
.
add_subplot
(
211
)
ax2
=
fig2
.
add_subplot
(
212
)
fig2
.
canvas
.
mpl_connect
(
'figure_enter_event'
,
enter_figure
)
fig2
.
canvas
.
mpl_connect
(
'figure_leave_event'
,
leave_figure
)
fig2
.
canvas
.
mpl_connect
(
'axes_enter_event'
,
enter_axes
)
fig2
.
canvas
.
mpl_connect
(
'axes_leave_event'
,
leave_axes
)
plt
.
show
()
```
## 对象拾取
你可以通过设置 Artist 的
`picker `
属性(例如,matplotlib
`Line2D`
,
`Text`
,
`Patch`
,
`Polygon`
,
`AxesImage`
等)来启用选择,
`picker `
属性有多种含义:
`None`
选择对于该 Artist 已禁用(默认)
`boolean`
如果为
`True`
,则启用选择,当鼠标移动到该 Artist 上方时,会触发事件
`float`
如果选择器是数字,则将其解释为点的 ε 公差,并且如果其数据在鼠标事件的 ε 内,则 Artist 将触发事件。 对于像线条和 Patch 集合的一些 Artist ,Artist 可以向生成的选择事件提供附加数据,例如,在选择事件的 ε 内的数据的索引。
函数
如果拾取器是可调用的,则它是用户提供的函数,用于确定艺术家是否被鼠标事件击中。 签名为
`hit, props = picker(artist, mouseevent)`
,用于测试是否命中。 如果鼠标事件在 Artist 上,返回
`hit = True`
,
`props`
是一个属性字典,它们会添加到
`PickEvent`
属性。
通过设置
`picker`
属性启用对艺术家进行拾取后,你需要连接到图画布的
`pick_event`
,以便在鼠标按下事件中获取拾取回调。 例如:
```
py
def
pick_handler
(
event
):
mouseevent
=
event
.
mouseevent
artist
=
event
.
artist
# now do something with this...
```
传给你的回调的
`PickEvent`
事件永远有两个属性:
`mouseevent `
是生成拾取事件的鼠标事件。鼠标事件具有像
`x`
和
`y`
(显示空间中的坐标,例如,距离左,下的像素)和
`xdata`
,
`ydata`
(数据空间中的坐标)的属性。 此外,你可以获取有关按下哪些按钮,按下哪些键,鼠标在哪个轴域上面等信息。详细信息请参阅
`matplotlib.backend_bases.MouseEvent`
。
`artist`
生成拾取事件的
`Artist `
。
另外,像
`Line2D`
和
`PatchCollection`
的某些 Artist 可以将附加的元数据(如索引)附加到满足选择器标准的数据中(例如,行中在指定 ε 容差内的所有点)
### 简单拾取示例
在下面的示例中,我们将行选择器属性设置为标量,因此它表示以点为单位的容差(72 点/英寸)。 当拾取事件位于距离线条的容差范围时,将调用
`onpick`
回调函数,并且带有在拾取距离容差内的数据顶点索引。 我们的
`onpick`
回调函数只打印在拾取位置上的数据。 不同的 matplotlib Artist 可以将不同的数据附加到
`PickEvent`
。 例如,
`Line2D`
将
`ind`
属性作为索引附加到拾取点下面的行数据中。 有关
`Line`
的
`PickEvent`
属性的详细信息,请参阅
`pick()`
。 这里是代码:
```
py
import
numpy
as
np
import
matplotlib.pyplot
as
plt
fig
=
plt
.
figure
()
ax
=
fig
.
add_subplot
(
111
)
ax
.
set_title
(
'click on points'
)
line
,
=
ax
.
plot
(
np
.
random
.
rand
(
100
),
'o'
,
picker
=
5
)
# 5 points tolerance
def
onpick
(
event
):
thisline
=
event
.
artist
xdata
=
thisline
.
get_xdata
()
ydata
=
thisline
.
get_ydata
()
ind
=
event
.
ind
points
=
tuple
(
zip
(
xdata
[
ind
],
ydata
[
ind
]))
print
(
'onpick points:'
,
points
)
fig
.
canvas
.
mpl_connect
(
'pick_event'
,
onpick
)
plt
.
show
()
```
### 拾取练习
创建含有 100 个数组的数据集,包含 1000 个高斯随机数,并计算每个数组的样本平均值和标准差(提示:
`numpy`
数组具有
`mean`
和
`std`
方法),并制作 100 个均值与 100 个标准的
`xy`
标记图。 将绘图命令创建的线条连接到拾取事件,并绘制数据的原始时间序列,这些数据生成了被点击的点。 如果在被点击的点的容差范围内存在多于一个点,则可以使用多个子图来绘制多个时间序列。
练习的解决方案:
```
py
"""
compute the mean and stddev of 100 data sets and plot mean vs stddev.
When you click on one of the mu, sigma points, plot the raw data from
the dataset that generated the mean and stddev
"""
import
numpy
as
np
import
matplotlib.pyplot
as
plt
X
=
np
.
random
.
rand
(
100
,
1000
)
xs
=
np
.
mean
(
X
,
axis
=
1
)
ys
=
np
.
std
(
X
,
axis
=
1
)
fig
=
plt
.
figure
()
ax
=
fig
.
add_subplot
(
111
)
ax
.
set_title
(
'click on point to plot time series'
)
line
,
=
ax
.
plot
(
xs
,
ys
,
'o'
,
picker
=
5
)
# 5 points tolerance
def
onpick
(
event
):
if
event
.
artist
!=
line
:
return
True
N
=
len
(
event
.
ind
)
if
not
N
:
return
True
figi
=
plt
.
figure
()
for
subplotnum
,
dataind
in
enumerate
(
event
.
ind
):
ax
=
figi
.
add_subplot
(
N
,
1
,
subplotnum
+
1
)
ax
.
plot
(
X
[
dataind
])
ax
.
text
(
0.05
,
0.9
,
'mu=%1.3f
\n
sigma=%1.3f'
%
(
xs
[
dataind
],
ys
[
dataind
]),
transform
=
ax
.
transAxes
,
va
=
'top'
)
ax
.
set_ylim
(
-
0.5
,
1.5
)
figi
.
show
()
return
True
fig
.
canvas
.
mpl_connect
(
'pick_event'
,
onpick
)
plt
.
show
()
```
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录