-1

I'm using matplotlib with python and tkinter. My graph plots 3 lines. Here is the related code:

    fig = plt.Figure(figsize=(8, 6), dpi=92)
    canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
    canvas.get_tk_widget().grid(row=2, column=3, columnspan=2, rowspan=2)
    plot1 = fig.add_subplot(111)
    plot1.set_ylim(40, 210)
    plot2 = plot1.twinx()
    sysline = plot1.plot(listDateTime, listSystolic, color='orange', label = "Systolic", linewidth=2, linestyle='solid')
    dialine = plot1.plot(listDateTime, listDiastolic, color='red', label = "Diastolic", linewidth=2, linestyle='solid')
    weightline = plot2.plot(listDateTime, listWeight, color='blue', label = "Weight", linewidth=2, linestyle='dotted')
    plot2.set_ylim(160, 220)
    for label in plot1.xaxis.get_ticklabels():
        label.set_rotation(-90)
    plot1.xaxis.set_major_formatter(ticker.FuncFormatter(getDateString))
    plot1.set_xlabel("Test Date")
    plot1.set_ylabel("Pressure (mmHg)")
    plot2.set_ylabel("Weight (lbs)")
    plot1.set_title(graphTitle)
    plot1.legend(loc='upper left')
    plot2.legend(loc='upper right')
    plot1.grid(True)
#    plot1.set_autoscale_on(True)
#    plot2.set_autoscale_on(True)
    plt.tight_layout()
    canvas.draw()

When I interactively load in an updated dataset, the new lines overlay the old ones, and I would prefer to delete the old lines and replot the data.

Because it's on a subplot, this approach doesn't work: line1 = plot1.pop(0) line1.remove() line2 = plot2.pop(0) line2.remove()

I've tried getting handles for the lines and using .remove(), which doesn't raise an error. But it doesn't have any effect, either.

Does .remove() not happen until the graph is redrawn? What am I missing here?

Joey Riso
  • 11
  • 1

1 Answers1

0

Your non-complete example does not work in replacing/deleting the previous lines in the plot because you're not using plot1.clear() and plot2.clear().

See this question for more information: Clearing a subplot in Matplotlib

Now, given you're doing this to make an "interactive" plot, here is a working POC for doing that:

import tkinter as tk
from tkinter import messagebox
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt


def update_plot():
    data = entry.get()
    values = [float(val) for val in data.split(',')]
    n = len(values)
    try:
        x = [i * 10 / (n - 1) for i in range(n)]
    except ZeroDivisionError:
        messagebox.showerror("error", "Using less than two values won't work")
        return  # trick to prevent the Zero Division error

    y = values

    # This is what replace the previous lines with newer ones
    plot1.clear()
    plot2.clear()

    # Plotting new random lines
    plot1.plot(x, y, color='orange', label="random line 1", linewidth=2, linestyle='solid')
    plot1.plot(x, [val * 0.8 for val in y], color='red', label="random line 2", linewidth=2, linestyle='solid')
    plot2.plot(x, [val * 2 for val in y], color='blue', label="random line 3", linewidth=2, linestyle='dotted')

    # Setting plot properties and redrawing the canvas
    plot1.set_ylim(0, max(y) + 1)
    plot2.set_ylim(0, max(y) * 2 + 1)
    plot1.set_xlabel("X")
    plot1.set_ylabel("Y1")
    plot2.set_ylabel("Y2")
    plot1.set_title("Updated Graph")
    plot1.legend(loc='upper left')
    plot2.legend(loc='upper right')
    plot1.grid(True)
    plt.tight_layout()

    canvas.draw()


root = tk.Tk()
root.title("Dynamic Plot")

entry = tk.Entry(root)
entry.pack()

fig = Figure(figsize=(8, 6), dpi=92)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack()

plot1 = fig.add_subplot(111)
plot2 = plot1.twinx()

update_button = tk.Button(root, text="Update Plot", command=update_plot)
update_button.pack()

root.mainloop()

The above will only work if you use more than one value separated by commas, such as 1,0 or 0,1 or 200, 0, 14, etc

Nordine Lotfi
  • 463
  • 2
  • 5
  • 20
  • The use of clear() seems to have unintended consequences - clearing plot1 removes all plot lines and the legends for sysline and dialine, and removes weightline but leaves the weightline legend. However I'm working on a different approach which may be fruitful. – Joey Riso May 23 '23 at 11:38
  • I see, this is indeed weird. Maybe try to base what you want to do with the proof of concept I made above? Might work as intended, since what I did seems to already work like you would want @JoeyRiso If not, feel free to tell me more details and I could improve my answer? – Nordine Lotfi May 23 '23 at 11:42
  • Also, remember you did not provide a full working example (like the one I made, which work as is). SO I couldn't know whether using clear() would have an adverse effect :) Next time, feel free to make what is commonly called as an [MRE](https://stackoverflow.com/help/minimal-reproducible-example) it will help you and the answerer better. @JoeyRiso – Nordine Lotfi May 23 '23 at 11:47