Edición interactiva en el lienzo de Matplotlib

PythonPythonBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Esta práctica te guiará a través de un ejemplo de una aplicación cruzada de GUI que utiliza el control de eventos de Matplotlib para interactuar y modificar objetos en el lienzo. Aprenderás cómo editar una trayectoria en un gráfico, arrastrando marcadores con el mouse y alternando su visibilidad.

Consejos sobre la VM

Una vez finalizada la inicialización de la VM, haz clic en la esquina superior izquierda para cambiar a la pestaña Cuaderno y acceder a Jupyter Notebook para practicar.

A veces, es posible que tengas que esperar unos segundos a que Jupyter Notebook termine de cargarse. La validación de operaciones no puede automatizarse debido a las limitaciones de Jupyter Notebook.

Si tienes problemas durante el aprendizaje, no dudes en preguntar a Labby. Proporciona retroalimentación después de la sesión y resolveremos rápidamente el problema para ti.

Importar bibliotecas

En este paso, importamos las bibliotecas necesarias para la práctica. Utilizamos Matplotlib para crear el gráfico y manejar 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

Crear el gráfico

En este paso, creamos un gráfico con una trayectoria verde y bordes amarillos, utilizando los datos de la trayectoria proporcionados. Luego agregamos un objeto PathPatch al gráfico, que representa la trayectoria.

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)

Crear la clase PathInteractor

En este paso, creamos la clase PathInteractor, que maneja las devoluciones de llamada de eventos para el objeto de trayectoria. Esta clase nos permite editar interactivamente la trayectoria arrastrando marcadores en el gráfico.

class PathInteractor:
    """
    Un editor de trayectorias.

    Presiona 't' para alternar los marcadores de vértices encendidos y apagados. Cuando los marcadores de vértices están encendidos,
    se pueden arrastrar con el mouse.
    """

    showverts = True
    epsilon = 5  ## distancia máxima en píxeles para considerarse un golpe en un 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  ## el vértice activo

        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):
        """
        Devuelve el índice del punto más cercano a la posición del evento o *None*
        si ningún punto está a una distancia de ``self.epsilon`` de la posición del evento.
        """
        xy = self.pathpatch.get_path().vertices
        xyt = self.pathpatch.get_transform().transform(xy)  ## a coordenadas de visualización
        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):
        """Devolución de llamada para dibujos."""
        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):
        """Devolución de llamada para pulsaciones del botón del 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):
        """Devolución de llamada para liberaciones del botón del mouse."""
        if (event.button!= MouseButton.LEFT
                or not self.showverts):
            return
        self._ind = None

    def on_key_press(self, event):
        """Devolución de llamada para pulsaciones de teclas."""
        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):
        """Devolución de llamada para movimientos del 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)

Crear el interactuador de trayectoria

En este paso, creamos una instancia de la clase PathInteractor, pasando el objeto PathPatch que creamos anteriormente.

interactor = PathInteractor(patch)

Establecer propiedades del gráfico

En este paso, establecemos el título y los límites de los ejes para el gráfico.

ax.set_title('drag vertices to update path')
ax.set_xlim(-3, 4)
ax.set_ylim(-3, 4)

Mostrar el gráfico

En este paso, mostramos el gráfico en la pantalla.

plt.show()

Resumen

En este laboratorio, aprendimos cómo crear un gráfico interactivo que nos permite editar una trayectoria arrastrando marcadores en el gráfico. Utilizamos la biblioteca Matplotlib para crear el gráfico y manejar eventos, y creamos una clase personalizada para manejar las devoluciones de llamada de eventos. Siguiendo estos pasos, puedes crear tus propios gráficos interactivos con Matplotlib.