Introdução
Este laboratório irá guiá-lo através de um exemplo de uma aplicação cross-GUI utilizando o tratamento de eventos do Matplotlib para interagir e modificar objetos na tela (canvas). Você aprenderá como editar um caminho (path) em um gráfico, arrastando marcadores com o mouse e alternando sua visibilidade.
Dicas para a VM
Após a inicialização da VM, clique no canto superior esquerdo para mudar para a aba Notebook e acessar o Jupyter Notebook para praticar.
Às vezes, pode ser necessário aguardar alguns segundos para que o Jupyter Notebook termine de carregar. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.
Se você enfrentar problemas durante o aprendizado, sinta-se à vontade para perguntar ao Labby. Forneça feedback após a sessão, e resolveremos o problema prontamente para você.
Importar Bibliotecas
Nesta etapa, importamos as bibliotecas necessárias para o laboratório. Usamos o Matplotlib para criar o gráfico e tratar eventos.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backend_bases import MouseButton
from matplotlib.patches import PathPatch
from matplotlib.path import Path
Criar o Gráfico
Nesta etapa, criamos um gráfico com um caminho verde e bordas amarelas, usando os dados do caminho fornecidos. Em seguida, adicionamos um objeto PathPatch ao gráfico, que representa o caminho.
fig, ax = plt.subplots()
pathdata = [
(Path.MOVETO, (1.58, -2.57)),
(Path.CURVE4, (0.35, -1.1)),
(Path.CURVE4, (-1.75, 2.0)),
(Path.CURVE4, (0.375, 2.0)),
(Path.LINETO, (0.85, 1.15)),
(Path.CURVE4, (2.2, 3.2)),
(Path.CURVE4, (3, 0.05)),
(Path.CURVE4, (2.0, -0.5)),
(Path.CLOSEPOLY, (1.58, -2.57)),
]
codes, verts = zip(*pathdata)
path = Path(verts, codes)
patch = PathPatch(
path, facecolor='green', edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)
Criar a Classe PathInteractor
Nesta etapa, criamos a classe PathInteractor, que lida com os retornos de chamada de eventos para o objeto caminho. Esta classe nos permite editar interativamente o caminho arrastando marcadores no gráfico.
class PathInteractor:
"""
Um editor de caminho.
Pressione 't' para alternar os marcadores de vértice. Quando os marcadores de vértice estão ativados,
eles podem ser arrastados com o mouse.
"""
showverts = True
epsilon = 5 ## distância máxima em pixels para contar como um acerto de vértice
def __init__(self, pathpatch):
self.ax = pathpatch.axes
canvas = self.ax.figure.canvas
self.pathpatch = pathpatch
self.pathpatch.set_animated(True)
x, y = zip(*self.pathpatch.get_path().vertices)
self.line, = ax.plot(
x, y, marker='o', markerfacecolor='r', animated=True)
self._ind = None ## o vértice ativo
canvas.mpl_connect('draw_event', self.on_draw)
canvas.mpl_connect('button_press_event', self.on_button_press)
canvas.mpl_connect('key_press_event', self.on_key_press)
canvas.mpl_connect('button_release_event', self.on_button_release)
canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
self.canvas = canvas
def get_ind_under_point(self, event):
"""
Retorna o índice do ponto mais próximo da posição do evento ou *None*
se nenhum ponto estiver dentro de ``self.epsilon`` da posição do evento.
"""
xy = self.pathpatch.get_path().vertices
xyt = self.pathpatch.get_transform().transform(xy) ## para exibir coordenadas
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)
ind = d.argmin()
return ind if d[ind] < self.epsilon else None
def on_draw(self, event):
"""Callback para desenhos."""
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.draw_artist(self.pathpatch)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)
def on_button_press(self, event):
"""Callback para pressionamentos de botão do mouse."""
if (event.inaxes is None
or event.button != MouseButton.LEFT
or not self.showverts):
return
self._ind = self.get_ind_under_point(event)
def on_button_release(self, event):
"""Callback para liberações de botão do mouse."""
if (event.button != MouseButton.LEFT
or not self.showverts):
return
self._ind = None
def on_key_press(self, event):
"""Callback para pressionamentos de tecla."""
if not event.inaxes:
return
if event.key == 't':
self.showverts = not self.showverts
self.line.set_visible(self.showverts)
if not self.showverts:
self._ind = None
self.canvas.draw()
def on_mouse_move(self, event):
"""Callback para movimentos do mouse."""
if (self._ind is None
or event.inaxes is None
or event.button != MouseButton.LEFT
or not self.showverts):
return
vertices = self.pathpatch.get_path().vertices
vertices[self._ind] = event.xdata, event.ydata
self.line.set_data(zip(*vertices))
self.canvas.restore_region(self.background)
self.ax.draw_artist(self.pathpatch)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)
Criar o Path Interactor
Nesta etapa, criamos uma instância da classe PathInteractor, passando o objeto PathPatch que criamos anteriormente.
interactor = PathInteractor(patch)
Definir as Propriedades do Gráfico
Nesta etapa, definimos o título e os limites dos eixos para o gráfico.
ax.set_title('drag vertices to update path')
ax.set_xlim(-3, 4)
ax.set_ylim(-3, 4)
Mostrar o Gráfico
Nesta etapa, mostramos o gráfico na tela.
plt.show()
Resumo
Neste laboratório, aprendemos como criar um gráfico interativo que nos permite editar um caminho arrastando marcadores no gráfico. Usamos a biblioteca Matplotlib para criar o gráfico e lidar com eventos, e criamos uma classe personalizada para lidar com os retornos de chamada (callbacks) de eventos. Seguindo estas etapas, você pode criar seus próprios gráficos interativos com Matplotlib.