matplotlib画一条可拖动的曲线

可以修改draw_line传入的n值,决定画几条曲线

import matplotlib
#此处在打包时会用到
matplotlib.use('TkAgg')
import sys,os
import time
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import numpy as np
from scipy.interpolate import interp1d
from matplotlib.widgets import Button


# 用于创建可拖动的线条对象,并处理鼠标事件。
class DraggableLine:
    def __init__(self, ax, x, y):
        self.ax = ax
        self.x = x
        self.y = y
        self.line, = ax.plot(x, y, marker='o', markersize=5, markerfacecolor='red', linestyle='-', picker=5)
        self.cid_click = self.line.figure.canvas.mpl_connect('button_press_event', self.on_click)
        self.cid_release = self.line.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cid_motion = self.line.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
        self.cid_dblclick = self.line.figure.canvas.mpl_connect('button_press_event', self.on_dblclick)
        self.selected_point = None
        self.annotation = self.ax.annotate('', xy=(0, 0), xytext=(10, 10),
                                           textcoords='offset points', bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                                           arrowprops=dict(arrowstyle='->'))
        self.annotation.set_visible(False)
        self.selected_points = []
        self.newx = []
        self.newy = []
        self.modify_xdata = []
        self.modify_ydata = []

    # 检测鼠标点击事件,判断是否选中某个点
    def on_click(self, event):
        # 判断事件是否在自己ax区间,不是发生在自己ax区间,不理会
        if event.inaxes != self.ax:
            return
        if event.inaxes != self.line.axes: return
        contains, attr = self.line.contains(event)
        if contains:
            self.selected_point = attr['ind'][0]
        else:
            self.selected_point = None
        self.update_annotation(event)

    # 释放鼠标按钮时,取消选中点。
    def on_release(self, event):
        # 判断事件是否在自己ax区间,不是发生在自己ax区间,不理会
        if event.inaxes != self.ax:
            return
        self.selected_point = None
        xdata = list(self.line.get_xdata())
        ydata = list(self.line.get_ydata())
        self.line.set_data(xdata, ydata)
        self.line.figure.canvas.draw()
        self.update_curve()
        self.annotation.set_visible(False)

    # 拖动鼠标时,更新选中点的纵坐标。
    def on_motion(self, event):
        # 判断事件是否在自己ax区间,不是发生在自己ax区间,不理会
        if event.inaxes != self.ax:
            return
        if self.selected_point is None: return
        if event.inaxes != self.line.axes: return
        ydata = self.line.get_ydata()
        ydata[self.selected_point] = int(event.ydata)
        if self.selected_point not in self.selected_points:
            self.selected_points.append(self.selected_point)
        self.line.set_ydata(ydata)
        self.line.figure.canvas.draw()
        self.update_annotation(event)
        self.update_curve()

    # 更新鼠标悬停时显示的坐标注释。
    def update_annotation(self, event):
        if event.inaxes != self.ax:
            return
        x = self.line.get_xdata()[self.selected_point]
        y = self.line.get_ydata()[self.selected_point]
        # self.annotation.xy = (event.xdata, event.ydata)
        self.annotation.xy = (x, y)
        text = f'({x}, {y})'
        self.annotation.set_text(text)
        self.annotation.set_visible(True)
        self.line.figure.canvas.draw()

    # 检测鼠标双击事件,左键双击添加锚点,右键双击移除锚点。
    def on_dblclick(self, event):
        # 判断事件是否在自己ax区间,不是发生在自己ax区间,不理会
        if event.inaxes != self.ax:
            return
        if event.dblclick:
            if event.button == 1:  # Left double click to add point
                self.add_point(int(event.xdata), int(event.ydata))
            elif event.button == 3:  # Right double click to remove point
                self.remove_point(event.xdata)
            xdata = list(self.line.get_xdata())
            ydata = list(self.line.get_ydata())
            self.line.set_data(xdata, ydata)
            self.line.figure.canvas.draw()
            self.update_curve()

    # 添加新的锚点,并保持锚点按横坐标排序。
    def add_point(self, x, y):
        xdata = list(self.line.get_xdata())
        ydata = list(self.line.get_ydata())
        xdata.append(x)
        ydata.append(y)
        sorted_indices = np.argsort(xdata)
        xdata = np.array(xdata)[sorted_indices]
        ydata = np.array(ydata)[sorted_indices]
        self.line.set_data(xdata, ydata)
        self.update_curve()

    # 移除指定横坐标的锚点
    def remove_point(self, x):
        xdata, ydata = self.get_data()
        distances = np.abs(np.array(xdata) - x)
        # 获取最小值对应的索引
        closest_index = np.argmin(distances)
        if distances[closest_index] < 0.5:  # 如果距离足够近
            xdata.pop(closest_index)
            ydata.pop(closest_index)
            self.line.set_data(xdata, ydata)
            self.update_curve()

    # 更新插值曲线,使用三次样条插值(cubic interpolation)连接锚点。
    def update_curve(self):
        xdata, ydata = self.get_data()
        if len(xdata) > 1:
            f = interp1d(xdata, ydata, kind=3)
            xnew = np.linspace(min(xdata), max(xdata), num=257, endpoint=True)
            ynew = f(xnew)
            self.modify_xdata = xnew
            self.modify_ydata = ynew
            self.ax.lines[-1].set_data(xnew, ynew)
            self.line.figure.canvas.draw()

    # 获取当前线条的坐标数据
    def get_data(self):
        return self.line.get_xdata(), self.line.get_ydata()

# xx 是横坐标数组
# yy是纵坐标数组
# x_points 是x轴锚点数组
# y_points 是y轴锚点数组
def draw_line(x, yy, x_point, y_points, n):
    # 设置绘图面板的显示尺寸9*100
    plt.rcParams['figure.figsize'] = (6, 6)
    # 纵向排列
    fig, ax = plt.subplots(n, 1)
    print(n)
    if n == 1:
        ax.set_title(rf'Draggable Line {0}')
        ax.set_xlim(-5, 12)
        ax.set_ylim(-5, 100)
    else:
        for i in range(0, n):
            ax[i].set_title(rf'Draggable Line {i}')
            ax[i].set_xlim(-5, 12)
            ax[i].set_ylim(-5, 100)

    if 1 == n:
        original_line = ax.plot(x, yy[0], linestyle='--', color='blue', label=f'Original Line{0}')
        draggable_lines = DraggableLine(ax, x_point, y_points[0])
        p_line, = ax.plot(x_point, y_points[0], linestyle='-', color='red', label=f'Modified line{0}')
        plt.legend()
        plt.show()
    else:
        # 画原始曲线(虚线)
        original_lines = [0] * n
        for i in range(0, n):
            original_lines[i], = ax[i].plot(x, yy[i], linestyle='--', color='blue', label=f'Original line{i}')

        # 创建可拖动的线条
        draggable_lines = [0] * n
        red_lines = [0] * n
        for i in range(0, n):
            # 添加一条用于显示插值曲线的实线
            draggable_lines[i] = DraggableLine(ax[i], x_point, y_points[i])
            p_line, = ax[i].plot(x_point, y_points[i], linestyle='-', color='red', label=f'Modified line{i}')

        plt.legend()
        plt.show()
if __name__ == '__main__':
    xx = [0,1,2,3,4,5,6,7,8,9]
    yy = []
    y1 = [-1,2,5,8,11,14,17,20,23,26]
    yy.append(y1)
    y2 = [20, 30, 35, 40, 45, 50, 55, 65, 75, 80]
    yy.append(y2)
    x_points = [0,2,4,6,8]
    y_points = []
    y_point1 = [-1,5,11,17,23]
    y_points.append(y_point1)
    y_point2 = [20,35,45,55,75]
    y_points.append(y_point2)
    draw_line(xx, yy, x_points, y_points, 2)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值