matplotlib plot not updating after the plot is first shown with .show

23 views Asked by At

I'm new to matplotlib and can't find out how to update my plot once I enter new values for the lines displayed in the plot. Showing the plots with the values that I entered first works without an issue.

main.py

from two_dof_plotter import MechanismPlotter
import time

# Create an object of MechanismPlotter
two_dof_robot_plot = MechanismPlotter(0.25, 0.3, 0.1, 0, 0)
two_dof_robot_plot.start_plot()
two_dof_robot_plot.draw_required_work_envelope(0, 0)
two_dof_robot_plot.draw_mechanism_with_inverse_kinematic(0.5, -0.0375)
two_dof_robot_plot.show_plot()

time.sleep(2)

two_dof_robot_plot.draw_mechanism_with_inverse_kinematic(0.2, -0.0375)

two_dof_plotter.py

import matplotlib.pyplot as plt
import numpy as np

class MechanismPlotter:
    def __init__(self, upper_arm_length, lower_arm_length, lever_arm_length, gripper_x_offset, gripper_y_offset):
        self.lower_arm_length = lower_arm_length
        self.upper_arm_length = upper_arm_length
        self.lever_arm_length = lever_arm_length
        self.gripper_x_offset = gripper_x_offset
        self.gripper_y_offset = gripper_y_offset
        self.ploted_lines = []
        self.fig, self.ax = plt.subplots()

    def start_plot(self):
        self.ax.set_aspect('equal')
        self.ax.set_xlim(-0.6, 0.6)
        self.ax.set_ylim(-0.6,0.6)
        self.ax.set_xlabel('X')
        self.ax.set_ylabel('Y')
        plt.grid(True)

    def draw_mechanism_with_forward_kinematic(self, upper_arm_angle, lower_arm_angle):
        # Calculate the coordinates of the lower and upper arm endpoints
        upper_arm_x = self.upper_arm_length * np.cos(upper_arm_angle)
        upper_arm_y = self.upper_arm_length * np.sin(upper_arm_angle)
        lower_arm_x = upper_arm_x + self.lower_arm_length * np.cos(lower_arm_angle)
        lower_arm_y = upper_arm_y + self.lower_arm_length * np.sin(lower_arm_angle)
        lever_arm_x = -(self.lever_arm_length * np.cos(lower_arm_angle))
        lever_arm_y = -(self.lever_arm_length * np.sin(lower_arm_angle))


        # Plot the lines
        if len(self.ploted_lines) > 0:
            self.ploted_lines[0].set_data([0, upper_arm_x], [0, upper_arm_y])
            self.ploted_lines[1].set_data([upper_arm_x, lower_arm_x], [upper_arm_y, lower_arm_y])
            self.ploted_lines[2].set_data([0, lever_arm_x], [0, lever_arm_y])
            self.ploted_lines[3].set_data([upper_arm_x, upper_arm_x + lever_arm_x], [upper_arm_y, upper_arm_y + lever_arm_y])
            self.ploted_lines[4].set_data([lever_arm_x, upper_arm_x + lever_arm_x], [lever_arm_y, upper_arm_y + lever_arm_y])
            # Draw the updated plot
            self.fig.canvas.draw()
        else:
            # Plot the lines
            line1, = self.ax.plot([0, upper_arm_x], [0, upper_arm_y], 'b-', linewidth=2)
            line2, = self.ax.plot([upper_arm_x, lower_arm_x], [upper_arm_y, lower_arm_y], 'r-', linewidth=2)
            line3, = self.ax.plot([0, lever_arm_x], [0, lever_arm_y], 'g-', linewidth=1)
            line4, = self.ax.plot([upper_arm_x, upper_arm_x + lever_arm_x], [upper_arm_y, upper_arm_y + lever_arm_y], 'g-', linewidth=1)
            line5, = self.ax.plot([lever_arm_x, upper_arm_x + lever_arm_x], [lever_arm_y, upper_arm_y + lever_arm_y], 'g-', linewidth=1)

            # Add the lines to the list of lines to remove
            self.ploted_lines.extend([line1, line2, line3, line4, line5])

        # Calculate the distance between the upper arm and the mechanism for the lower arm
        distance = np.sin(np.pi-(-lower_arm_angle)-upper_arm_angle) * self.lever_arm_length        


    def draw_mechanism_with_inverse_kinematic(self, x, y):
        # Calculate the inverse kinematic angles
        x = x - self.gripper_x_offset
        y = y - self.gripper_y_offset
        c = np.sqrt(x**2 + y**2)
        upper_arm_angle = np.arccos((self.upper_arm_length**2 + c**2 - self.lower_arm_length**2) / (2 * self.upper_arm_length * c)) + np.arctan(y/x)
        lower_arm_angle = upper_arm_angle + np.arccos((self.lower_arm_length**2 + self.upper_arm_length**2 - c**2) / (2 * self.lower_arm_length * self.upper_arm_length)) - np.pi

        # Draw the mechanism
        ploted_lines = self.draw_mechanism_with_forward_kinematic(upper_arm_angle, lower_arm_angle)
    
    
    def remove_ploted_mechanism(self, ploted_lines):
        for line in ploted_lines:
            line.remove()
        self.ax.clear()
        self.fig.canvas.draw()

    def draw_required_work_envelope(self, x_por_offset, y_por_offset):
        # Define the coordinates of the work envelope points
        x = [0.16+x_por_offset, 0.16+x_por_offset, 0.5+x_por_offset, 0.32+x_por_offset, 0.16+x_por_offset,]
        y = [0.06-y_por_offset, -0.0375-y_por_offset, -0.0375-y_por_offset, 0.06-y_por_offset, 0.06-y_por_offset,]

        # Plot the work envelope
        self.ax.fill_between(x, y, color='skyblue', alpha=0.5)


    def show_plot(self):
        plt.show()

what do i have to do to update it correctly after calling the method draw_mechanism_with_forward_kinematic a second time?

I already tried different implementations of the draw() function and tried with the .ion(), but so far I haven't found where my issue is.

1

There are 1 answers

1
raphael On BEST ANSWER

as I see it you have 3 options:

  1. call plt.show() at the very end
  2. use plt.show(block=False)
  3. switch to interactive-mode (by calling plt.ion() at the very beginning of the script) to avoid blocking the terminal in general

For interactive-mode, see: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.isinteractive.html#matplotlib.pyplot.isinteractive